Skip to content

Commit 777f88a

Browse files
committed
perf: Remove HashtableEx version validation and concurrency validation
1 parent b085b63 commit 777f88a

File tree

1 file changed

+4
-53
lines changed

1 file changed

+4
-53
lines changed

src/Uno.Foundation/Collections/HashtableEx.cs

Lines changed: 4 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212

1313
namespace Uno.Collections
1414
{
15+
/// <summary>
16+
/// Specialized version of <see cref="Hashtable"/> providing TryGetValue and single-threaded optimizations
17+
/// </summary>
1518
[DebuggerDisplay("Count = {Count}")]
1619
internal class HashtableEx : IEnumerable
1720
{
@@ -90,9 +93,6 @@ private struct bucket
9093
private int _loadsize;
9194
private float _loadFactor;
9295

93-
private volatile int _version;
94-
private volatile bool _isWriterInProgress;
95-
9696
private ICollection? _keys;
9797
private ICollection? _values;
9898

@@ -147,7 +147,6 @@ public HashtableEx(int capacity, float loadFactor)
147147
_buckets = new bucket[hashsize];
148148

149149
_loadsize = (int)(_loadFactor * hashsize);
150-
_isWriterInProgress = false;
151150
// Based on the current algorithm, loadsize must be less than hashsize.
152151
Debug.Assert(_loadsize < hashsize, "Invalid hashtable loadsize!");
153152
}
@@ -244,12 +243,9 @@ public virtual void Add(object key, object? value)
244243
// Removes all entries from this hashtable.
245244
public virtual void Clear()
246245
{
247-
Debug.Assert(!_isWriterInProgress, "Race condition detected in usages of Hashtable - multiple threads appear to be writing to a Hashtable instance simultaneously! Don't do that - use Hashtable.Synchronized.");
248-
249246
if (_count == 0 && _occupancy == 0)
250247
return;
251248

252-
_isWriterInProgress = true;
253249
for (int i = 0; i < _buckets.Length; i++)
254250
{
255251
_buckets[i].hash_coll = 0;
@@ -259,8 +255,6 @@ public virtual void Clear()
259255

260256
_count = 0;
261257
_occupancy = 0;
262-
UpdateVersion();
263-
_isWriterInProgress = false;
264258
}
265259

266260
// Clone returns a virtually identical copy of this hash table. This does
@@ -270,7 +264,6 @@ public virtual object Clone()
270264
{
271265
bucket[] lbuckets = _buckets;
272266
HashtableEx ht = new HashtableEx(_count, _keycomparer);
273-
ht._version = _version;
274267
ht._loadFactor = _loadFactor;
275268
ht._count = 0;
276269

@@ -478,8 +471,6 @@ public bool TryGetValue(object key, out object? value)
478471
int bucketNumber = (int)(seed % (uint)lbuckets.Length);
479472
do
480473
{
481-
int currentversion;
482-
483474
// A read operation on hashtable has three steps:
484475
// (1) calculate the hash and find the slot number.
485476
// (2) compare the hashcode, if equal, go to step 3. Otherwise end.
@@ -496,18 +487,8 @@ public bool TryGetValue(object key, out object? value)
496487
// Our memory model guarantee if we pick up the change in bucket from another processor,
497488
// we will see the 'isWriterProgress' flag to be true or 'version' is changed in the reader.
498489
//
499-
SpinWait spin = default;
500-
while (true)
501-
{
502-
// this is volatile read, following memory accesses can not be moved ahead of it.
503-
currentversion = _version;
504-
b = lbuckets[bucketNumber];
505-
506-
if (!_isWriterInProgress && (currentversion == _version))
507-
break;
508490

509-
spin.SpinOnce();
510-
}
491+
b = lbuckets[bucketNumber];
511492

512493
if (b.key == null)
513494
{
@@ -548,13 +529,6 @@ private void rehash()
548529
rehash(_buckets.Length);
549530
}
550531

551-
private void UpdateVersion()
552-
{
553-
// Version might become negative when version is int.MaxValue, but the oddity will be still be correct.
554-
// So we don't need to special case this.
555-
_version++;
556-
}
557-
558532
private void rehash(int newsize)
559533
{
560534
// reset occupancy
@@ -581,11 +555,8 @@ private void rehash(int newsize)
581555
}
582556

583557
// New bucket[] is good to go - replace buckets and other internal state.
584-
_isWriterInProgress = true;
585558
_buckets = newBuckets;
586559
_loadsize = (int)(_loadFactor * newsize);
587-
UpdateVersion();
588-
_isWriterInProgress = false;
589560
// minimum size of hashtable is 3 now and maximum loadFactor is 0.72 now.
590561
Debug.Assert(_loadsize < newsize, "Our current implementation means this is not possible.");
591562
}
@@ -719,13 +690,10 @@ private void Insert(object key, object? nvalue, bool add)
719690

720691
// We pretty much have to insert in this order. Don't set hash
721692
// code until the value & key are set appropriately.
722-
_isWriterInProgress = true;
723693
_buckets[bucketNumber].val = nvalue;
724694
_buckets[bucketNumber].key = key;
725695
_buckets[bucketNumber].hash_coll |= (int)hashcode;
726696
_count++;
727-
UpdateVersion();
728-
_isWriterInProgress = false;
729697

730698
return;
731699
}
@@ -740,10 +708,7 @@ private void Insert(object key, object? nvalue, bool add)
740708
{
741709
throw new ArgumentException("SR.Argument_AddingDuplicate__"); // SR.Format("SR.Argument_AddingDuplicate__", _buckets[bucketNumber].key, key));
742710
}
743-
_isWriterInProgress = true;
744711
_buckets[bucketNumber].val = nvalue;
745-
UpdateVersion();
746-
_isWriterInProgress = false;
747712

748713
return;
749714
}
@@ -767,13 +732,10 @@ private void Insert(object key, object? nvalue, bool add)
767732
{
768733
// We pretty much have to insert in this order. Don't set hash
769734
// code until the value & key are set appropriately.
770-
_isWriterInProgress = true;
771735
_buckets[emptySlotNumber].val = nvalue;
772736
_buckets[emptySlotNumber].key = key;
773737
_buckets[emptySlotNumber].hash_coll |= (int)hashcode;
774738
_count++;
775-
UpdateVersion();
776-
_isWriterInProgress = false;
777739

778740
return;
779741
}
@@ -822,8 +784,6 @@ public virtual void Remove(object key)
822784
throw new ArgumentNullException(nameof(key), "SR.ArgumentNull_Key");
823785
}
824786

825-
Debug.Assert(!_isWriterInProgress, "Race condition detected in usages of Hashtable - multiple threads appear to be writing to a Hashtable instance simultaneously! Don't do that - use Hashtable.Synchronized.");
826-
827787
// Assuming only one concurrent writer, write directly into buckets.
828788
uint hashcode = InitHash(key, _buckets.Length, out uint seed, out uint incr);
829789
int ntry = 0;
@@ -836,7 +796,6 @@ public virtual void Remove(object key)
836796
if (((b.hash_coll & 0x7FFFFFFF) == hashcode) &&
837797
KeyEquals(b.key, key))
838798
{
839-
_isWriterInProgress = true;
840799
// Clear hash_coll field, then key, then value
841800
_buckets[bn].hash_coll &= unchecked((int)0x80000000);
842801
if (_buckets[bn].hash_coll != 0)
@@ -849,8 +808,6 @@ public virtual void Remove(object key)
849808
}
850809
_buckets[bn].val = null; // Free object references sooner & simplify ContainsValue.
851810
_count--;
852-
UpdateVersion();
853-
_isWriterInProgress = false;
854811
return;
855812
}
856813
bn = (int)(((long)bn + incr) % (uint)_buckets.Length);
@@ -943,7 +900,6 @@ private sealed class HashtableEnumerator : IDictionaryEnumerator, ICloneable
943900
{
944901
private readonly HashtableEx _hashtable;
945902
private int _bucket;
946-
private readonly int _version;
947903
private bool _current;
948904
private readonly int _getObjectRetType; // What should GetObject return?
949905
private object? _currentKey;
@@ -957,7 +913,6 @@ internal HashtableEnumerator(HashtableEx hashtable, int getObjRetType)
957913
{
958914
_hashtable = hashtable;
959915
_bucket = hashtable._buckets.Length;
960-
_version = hashtable._version;
961916
_current = false;
962917
_getObjectRetType = getObjRetType;
963918
}
@@ -976,8 +931,6 @@ public object Key
976931

977932
public bool MoveNext()
978933
{
979-
if (_version != _hashtable._version)
980-
throw new InvalidOperationException("SR.InvalidOperation_EnumFailedVersion");
981934
while (_bucket > 0)
982935
{
983936
_bucket--;
@@ -1032,8 +985,6 @@ public object? Value
1032985

1033986
public void Reset()
1034987
{
1035-
if (_version != _hashtable._version)
1036-
throw new InvalidOperationException("SR.InvalidOperation_EnumFailedVersion");
1037988
_current = false;
1038989
_bucket = _hashtable._buckets.Length;
1039990
_currentKey = null;

0 commit comments

Comments
 (0)