12
12
13
13
namespace Uno . Collections
14
14
{
15
+ /// <summary>
16
+ /// Specialized version of <see cref="Hashtable"/> providing TryGetValue and single-threaded optimizations
17
+ /// </summary>
15
18
[ DebuggerDisplay ( "Count = {Count}" ) ]
16
19
internal class HashtableEx : IEnumerable
17
20
{
@@ -90,9 +93,6 @@ private struct bucket
90
93
private int _loadsize ;
91
94
private float _loadFactor ;
92
95
93
- private volatile int _version ;
94
- private volatile bool _isWriterInProgress ;
95
-
96
96
private ICollection ? _keys ;
97
97
private ICollection ? _values ;
98
98
@@ -147,7 +147,6 @@ public HashtableEx(int capacity, float loadFactor)
147
147
_buckets = new bucket [ hashsize ] ;
148
148
149
149
_loadsize = ( int ) ( _loadFactor * hashsize ) ;
150
- _isWriterInProgress = false ;
151
150
// Based on the current algorithm, loadsize must be less than hashsize.
152
151
Debug . Assert ( _loadsize < hashsize , "Invalid hashtable loadsize!" ) ;
153
152
}
@@ -244,12 +243,9 @@ public virtual void Add(object key, object? value)
244
243
// Removes all entries from this hashtable.
245
244
public virtual void Clear ( )
246
245
{
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
-
249
246
if ( _count == 0 && _occupancy == 0 )
250
247
return ;
251
248
252
- _isWriterInProgress = true ;
253
249
for ( int i = 0 ; i < _buckets . Length ; i ++ )
254
250
{
255
251
_buckets [ i ] . hash_coll = 0 ;
@@ -259,8 +255,6 @@ public virtual void Clear()
259
255
260
256
_count = 0 ;
261
257
_occupancy = 0 ;
262
- UpdateVersion ( ) ;
263
- _isWriterInProgress = false ;
264
258
}
265
259
266
260
// Clone returns a virtually identical copy of this hash table. This does
@@ -270,7 +264,6 @@ public virtual object Clone()
270
264
{
271
265
bucket [ ] lbuckets = _buckets ;
272
266
HashtableEx ht = new HashtableEx ( _count , _keycomparer ) ;
273
- ht . _version = _version ;
274
267
ht . _loadFactor = _loadFactor ;
275
268
ht . _count = 0 ;
276
269
@@ -478,8 +471,6 @@ public bool TryGetValue(object key, out object? value)
478
471
int bucketNumber = ( int ) ( seed % ( uint ) lbuckets . Length ) ;
479
472
do
480
473
{
481
- int currentversion ;
482
-
483
474
// A read operation on hashtable has three steps:
484
475
// (1) calculate the hash and find the slot number.
485
476
// (2) compare the hashcode, if equal, go to step 3. Otherwise end.
@@ -496,18 +487,8 @@ public bool TryGetValue(object key, out object? value)
496
487
// Our memory model guarantee if we pick up the change in bucket from another processor,
497
488
// we will see the 'isWriterProgress' flag to be true or 'version' is changed in the reader.
498
489
//
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 ;
508
490
509
- spin . SpinOnce ( ) ;
510
- }
491
+ b = lbuckets [ bucketNumber ] ;
511
492
512
493
if ( b . key == null )
513
494
{
@@ -548,13 +529,6 @@ private void rehash()
548
529
rehash ( _buckets . Length ) ;
549
530
}
550
531
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
-
558
532
private void rehash ( int newsize )
559
533
{
560
534
// reset occupancy
@@ -581,11 +555,8 @@ private void rehash(int newsize)
581
555
}
582
556
583
557
// New bucket[] is good to go - replace buckets and other internal state.
584
- _isWriterInProgress = true ;
585
558
_buckets = newBuckets ;
586
559
_loadsize = ( int ) ( _loadFactor * newsize ) ;
587
- UpdateVersion ( ) ;
588
- _isWriterInProgress = false ;
589
560
// minimum size of hashtable is 3 now and maximum loadFactor is 0.72 now.
590
561
Debug . Assert ( _loadsize < newsize , "Our current implementation means this is not possible." ) ;
591
562
}
@@ -719,13 +690,10 @@ private void Insert(object key, object? nvalue, bool add)
719
690
720
691
// We pretty much have to insert in this order. Don't set hash
721
692
// code until the value & key are set appropriately.
722
- _isWriterInProgress = true ;
723
693
_buckets [ bucketNumber ] . val = nvalue ;
724
694
_buckets [ bucketNumber ] . key = key ;
725
695
_buckets [ bucketNumber ] . hash_coll |= ( int ) hashcode ;
726
696
_count ++ ;
727
- UpdateVersion ( ) ;
728
- _isWriterInProgress = false ;
729
697
730
698
return ;
731
699
}
@@ -740,10 +708,7 @@ private void Insert(object key, object? nvalue, bool add)
740
708
{
741
709
throw new ArgumentException ( "SR.Argument_AddingDuplicate__" ) ; // SR.Format("SR.Argument_AddingDuplicate__", _buckets[bucketNumber].key, key));
742
710
}
743
- _isWriterInProgress = true ;
744
711
_buckets [ bucketNumber ] . val = nvalue ;
745
- UpdateVersion ( ) ;
746
- _isWriterInProgress = false ;
747
712
748
713
return ;
749
714
}
@@ -767,13 +732,10 @@ private void Insert(object key, object? nvalue, bool add)
767
732
{
768
733
// We pretty much have to insert in this order. Don't set hash
769
734
// code until the value & key are set appropriately.
770
- _isWriterInProgress = true ;
771
735
_buckets [ emptySlotNumber ] . val = nvalue ;
772
736
_buckets [ emptySlotNumber ] . key = key ;
773
737
_buckets [ emptySlotNumber ] . hash_coll |= ( int ) hashcode ;
774
738
_count ++ ;
775
- UpdateVersion ( ) ;
776
- _isWriterInProgress = false ;
777
739
778
740
return ;
779
741
}
@@ -822,8 +784,6 @@ public virtual void Remove(object key)
822
784
throw new ArgumentNullException ( nameof ( key ) , "SR.ArgumentNull_Key" ) ;
823
785
}
824
786
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
-
827
787
// Assuming only one concurrent writer, write directly into buckets.
828
788
uint hashcode = InitHash ( key , _buckets . Length , out uint seed , out uint incr ) ;
829
789
int ntry = 0 ;
@@ -836,7 +796,6 @@ public virtual void Remove(object key)
836
796
if ( ( ( b . hash_coll & 0x7FFFFFFF ) == hashcode ) &&
837
797
KeyEquals ( b . key , key ) )
838
798
{
839
- _isWriterInProgress = true ;
840
799
// Clear hash_coll field, then key, then value
841
800
_buckets [ bn ] . hash_coll &= unchecked ( ( int ) 0x80000000 ) ;
842
801
if ( _buckets [ bn ] . hash_coll != 0 )
@@ -849,8 +808,6 @@ public virtual void Remove(object key)
849
808
}
850
809
_buckets [ bn ] . val = null ; // Free object references sooner & simplify ContainsValue.
851
810
_count -- ;
852
- UpdateVersion ( ) ;
853
- _isWriterInProgress = false ;
854
811
return ;
855
812
}
856
813
bn = ( int ) ( ( ( long ) bn + incr ) % ( uint ) _buckets . Length ) ;
@@ -943,7 +900,6 @@ private sealed class HashtableEnumerator : IDictionaryEnumerator, ICloneable
943
900
{
944
901
private readonly HashtableEx _hashtable ;
945
902
private int _bucket ;
946
- private readonly int _version ;
947
903
private bool _current ;
948
904
private readonly int _getObjectRetType ; // What should GetObject return?
949
905
private object ? _currentKey ;
@@ -957,7 +913,6 @@ internal HashtableEnumerator(HashtableEx hashtable, int getObjRetType)
957
913
{
958
914
_hashtable = hashtable ;
959
915
_bucket = hashtable . _buckets . Length ;
960
- _version = hashtable . _version ;
961
916
_current = false ;
962
917
_getObjectRetType = getObjRetType ;
963
918
}
@@ -976,8 +931,6 @@ public object Key
976
931
977
932
public bool MoveNext ( )
978
933
{
979
- if ( _version != _hashtable . _version )
980
- throw new InvalidOperationException ( "SR.InvalidOperation_EnumFailedVersion" ) ;
981
934
while ( _bucket > 0 )
982
935
{
983
936
_bucket -- ;
@@ -1032,8 +985,6 @@ public object? Value
1032
985
1033
986
public void Reset ( )
1034
987
{
1035
- if ( _version != _hashtable . _version )
1036
- throw new InvalidOperationException ( "SR.InvalidOperation_EnumFailedVersion" ) ;
1037
988
_current = false ;
1038
989
_bucket = _hashtable . _buckets . Length ;
1039
990
_currentKey = null ;
0 commit comments