@@ -257,14 +257,23 @@ public bool Contains(T item)
257257 {
258258 if ( _buckets != null )
259259 {
260+ int collisionCount = 0 ;
260261 int hashCode = InternalGetHashCode ( item ) ;
262+ Slot [ ] slots = _slots ;
261263 // see note at "HashSet" level describing why "- 1" appears in for loop
262- for ( int i = _buckets [ hashCode % _buckets . Length ] - 1 ; i >= 0 ; i = _slots [ i ] . next )
264+ for ( int i = _buckets [ hashCode % _buckets . Length ] - 1 ; i >= 0 ; i = slots [ i ] . next )
263265 {
264- if ( _slots [ i ] . hashCode == hashCode && _comparer . Equals ( _slots [ i ] . value , item ) )
266+ if ( slots [ i ] . hashCode == hashCode && _comparer . Equals ( slots [ i ] . value , item ) )
265267 {
266268 return true ;
267269 }
270+
271+ if ( collisionCount >= slots . Length )
272+ {
273+ // The chain of entries forms a loop, which means a concurrent update has happened.
274+ throw new InvalidOperationException ( SR . InvalidOperation_ConcurrentOperationsNotSupported ) ;
275+ }
276+ collisionCount ++ ;
268277 }
269278 }
270279 // either _buckets is null or wasn't found
@@ -293,26 +302,28 @@ public bool Remove(T item)
293302 int hashCode = InternalGetHashCode ( item ) ;
294303 int bucket = hashCode % _buckets . Length ;
295304 int last = - 1 ;
296- for ( int i = _buckets [ bucket ] - 1 ; i >= 0 ; last = i , i = _slots [ i ] . next )
305+ int collisionCount = 0 ;
306+ Slot [ ] slots = _slots ;
307+ for ( int i = _buckets [ bucket ] - 1 ; i >= 0 ; last = i , i = slots [ i ] . next )
297308 {
298- if ( _slots [ i ] . hashCode == hashCode && _comparer . Equals ( _slots [ i ] . value , item ) )
309+ if ( slots [ i ] . hashCode == hashCode && _comparer . Equals ( slots [ i ] . value , item ) )
299310 {
300311 if ( last < 0 )
301312 {
302313 // first iteration; update buckets
303- _buckets [ bucket ] = _slots [ i ] . next + 1 ;
314+ _buckets [ bucket ] = slots [ i ] . next + 1 ;
304315 }
305316 else
306317 {
307318 // subsequent iterations; update 'next' pointers
308- _slots [ last ] . next = _slots [ i ] . next ;
319+ slots [ last ] . next = slots [ i ] . next ;
309320 }
310- _slots [ i ] . hashCode = - 1 ;
321+ slots [ i ] . hashCode = - 1 ;
311322 if ( RuntimeHelpers . IsReferenceOrContainsReferences < T > ( ) )
312323 {
313- _slots [ i ] . value = default ( T ) ;
324+ slots [ i ] . value = default ( T ) ;
314325 }
315- _slots [ i ] . next = _freeList ;
326+ slots [ i ] . next = _freeList ;
316327
317328 _count -- ;
318329 _version ++ ;
@@ -327,6 +338,13 @@ public bool Remove(T item)
327338 }
328339 return true ;
329340 }
341+
342+ if ( collisionCount >= slots . Length )
343+ {
344+ // The chain of entries forms a loop, which means a concurrent update has happened.
345+ throw new InvalidOperationException ( SR . InvalidOperation_ConcurrentOperationsNotSupported ) ;
346+ }
347+ collisionCount ++ ;
330348 }
331349 }
332350 // either _buckets is null or wasn't found
@@ -1196,35 +1214,44 @@ private bool AddIfNotPresent(T value)
11961214
11971215 int hashCode = InternalGetHashCode ( value ) ;
11981216 int bucket = hashCode % _buckets . Length ;
1199-
1200- for ( int i = _buckets [ bucket ] - 1 ; i >= 0 ; i = _slots [ i ] . next )
1217+ int collisionCount = 0 ;
1218+ Slot [ ] slots = _slots ;
1219+ for ( int i = _buckets [ bucket ] - 1 ; i >= 0 ; i = slots [ i ] . next )
12011220 {
1202- if ( _slots [ i ] . hashCode == hashCode && _comparer . Equals ( _slots [ i ] . value , value ) )
1221+ if ( slots [ i ] . hashCode == hashCode && _comparer . Equals ( slots [ i ] . value , value ) )
12031222 {
12041223 return false ;
12051224 }
1225+
1226+ if ( collisionCount >= slots . Length )
1227+ {
1228+ // The chain of entries forms a loop, which means a concurrent update has happened.
1229+ throw new InvalidOperationException ( SR . InvalidOperation_ConcurrentOperationsNotSupported ) ;
1230+ }
1231+ collisionCount ++ ;
12061232 }
12071233
12081234 int index ;
12091235 if ( _freeList >= 0 )
12101236 {
12111237 index = _freeList ;
1212- _freeList = _slots [ index ] . next ;
1238+ _freeList = slots [ index ] . next ;
12131239 }
12141240 else
12151241 {
1216- if ( _lastIndex == _slots . Length )
1242+ if ( _lastIndex == slots . Length )
12171243 {
12181244 IncreaseCapacity ( ) ;
12191245 // this will change during resize
1246+ slots = _slots ;
12201247 bucket = hashCode % _buckets . Length ;
12211248 }
12221249 index = _lastIndex ;
12231250 _lastIndex ++ ;
12241251 }
1225- _slots [ index ] . hashCode = hashCode ;
1226- _slots [ index ] . value = value ;
1227- _slots [ index ] . next = _buckets [ bucket ] - 1 ;
1252+ slots [ index ] . hashCode = hashCode ;
1253+ slots [ index ] . value = value ;
1254+ slots [ index ] . next = _buckets [ bucket ] - 1 ;
12281255 _buckets [ bucket ] = index + 1 ;
12291256 _count ++ ;
12301257 _version ++ ;
@@ -1376,13 +1403,22 @@ private int InternalIndexOf(T item)
13761403 {
13771404 Debug . Assert ( _buckets != null , "_buckets was null; callers should check first" ) ;
13781405
1406+ int collisionCount = 0 ;
13791407 int hashCode = InternalGetHashCode ( item ) ;
1380- for ( int i = _buckets [ hashCode % _buckets . Length ] - 1 ; i >= 0 ; i = _slots [ i ] . next )
1408+ Slot [ ] slots = _slots ;
1409+ for ( int i = _buckets [ hashCode % _buckets . Length ] - 1 ; i >= 0 ; i = slots [ i ] . next )
13811410 {
1382- if ( ( _slots [ i ] . hashCode ) == hashCode && _comparer . Equals ( _slots [ i ] . value , item ) )
1411+ if ( ( slots [ i ] . hashCode ) == hashCode && _comparer . Equals ( slots [ i ] . value , item ) )
13831412 {
13841413 return i ;
13851414 }
1415+
1416+ if ( collisionCount >= slots . Length )
1417+ {
1418+ // The chain of entries forms a loop, which means a concurrent update has happened.
1419+ throw new InvalidOperationException ( SR . InvalidOperation_ConcurrentOperationsNotSupported ) ;
1420+ }
1421+ collisionCount ++ ;
13861422 }
13871423 // wasn't found
13881424 return - 1 ;
@@ -1500,34 +1536,44 @@ private bool AddOrGetLocation(T value, out int location)
15001536
15011537 int hashCode = InternalGetHashCode ( value ) ;
15021538 int bucket = hashCode % _buckets . Length ;
1503- for ( int i = _buckets [ bucket ] - 1 ; i >= 0 ; i = _slots [ i ] . next )
1539+ int collisionCount = 0 ;
1540+ Slot [ ] slots = _slots ;
1541+ for ( int i = _buckets [ bucket ] - 1 ; i >= 0 ; i = slots [ i ] . next )
15041542 {
1505- if ( _slots [ i ] . hashCode == hashCode && _comparer . Equals ( _slots [ i ] . value , value ) )
1543+ if ( slots [ i ] . hashCode == hashCode && _comparer . Equals ( slots [ i ] . value , value ) )
15061544 {
15071545 location = i ;
15081546 return false ; //already present
15091547 }
1548+
1549+ if ( collisionCount >= slots . Length )
1550+ {
1551+ // The chain of entries forms a loop, which means a concurrent update has happened.
1552+ throw new InvalidOperationException ( SR . InvalidOperation_ConcurrentOperationsNotSupported ) ;
1553+ }
1554+ collisionCount ++ ;
15101555 }
15111556 int index ;
15121557 if ( _freeList >= 0 )
15131558 {
15141559 index = _freeList ;
1515- _freeList = _slots [ index ] . next ;
1560+ _freeList = slots [ index ] . next ;
15161561 }
15171562 else
15181563 {
1519- if ( _lastIndex == _slots . Length )
1564+ if ( _lastIndex == slots . Length )
15201565 {
15211566 IncreaseCapacity ( ) ;
15221567 // this will change during resize
1568+ slots = _slots ;
15231569 bucket = hashCode % _buckets . Length ;
15241570 }
15251571 index = _lastIndex ;
15261572 _lastIndex ++ ;
15271573 }
1528- _slots [ index ] . hashCode = hashCode ;
1529- _slots [ index ] . value = value ;
1530- _slots [ index ] . next = _buckets [ bucket ] - 1 ;
1574+ slots [ index ] . hashCode = hashCode ;
1575+ slots [ index ] . value = value ;
1576+ slots [ index ] . next = _buckets [ bucket ] - 1 ;
15311577 _buckets [ bucket ] = index + 1 ;
15321578 _count ++ ;
15331579 _version ++ ;
0 commit comments