@@ -72,14 +72,13 @@ private struct CacheEntry
7272 // which can cause deadlocks during Unity's "Open Project: Open Scene" phase.
7373 private IRandom _random ;
7474 private IRandom Random => _random ??= PRNG . Instance ;
75+ private ReaderWriterLockSlim _lock ;
7576
7677#if SINGLE_THREADED
7778 private readonly Dictionary < TKey , int > _keyToIndex ;
7879#else
7980 private readonly ConcurrentDictionary < TKey , int > _keyToIndex ;
80- private readonly object _evictionLock = new object ( ) ;
8181#endif
82-
8382 private CacheEntry [ ] _entries ;
8483 private int _count ;
8584 private int _capacity ;
@@ -246,6 +245,7 @@ public Cache(CacheOptions<TKey, TValue> options)
246245 ) ;
247246#endif
248247
248+ _lock = new ReaderWriterLockSlim ( LockRecursionPolicy . NoRecursion ) ;
249249 _entries = new CacheEntry [ initialCapacity ] ;
250250
251251 InitializeFreeList ( ) ;
@@ -295,16 +295,35 @@ private void InitializeLinkedLists()
295295 /// <returns>True if the key was found and not expired, false otherwise.</returns>
296296 public bool TryGet ( TKey key , out TValue value )
297297 {
298- value = default ;
298+ if ( _disposed )
299+ {
300+ value = default ;
301+ return false ;
302+ }
303+
304+ _lock . EnterReadLock ( ) ;
305+ try
306+ {
307+ return TryGetUnlocked ( key , out value ) ;
308+ }
309+ finally
310+ {
311+ _lock . ExitReadLock ( ) ;
312+ }
313+ }
299314
315+ private bool TryGetUnlocked ( TKey key , out TValue value )
316+ {
300317 if ( key == null || _disposed )
301318 {
319+ value = default ;
302320 return false ;
303321 }
304322
305323 if ( ! _keyToIndex . TryGetValue ( key , out int index ) )
306324 {
307325 RecordMiss ( ) ;
326+ value = default ;
308327 return false ;
309328 }
310329
@@ -315,6 +334,7 @@ public bool TryGet(TKey key, out TValue value)
315334 EvictEntry ( index , EvictionReason . Expired ) ;
316335 RecordMiss ( ) ;
317336 RecordExpired ( ) ;
337+ value = default ;
318338 return false ;
319339 }
320340
@@ -339,30 +359,51 @@ public TValue GetOrAdd(TKey key, Func<TKey, TValue> factory)
339359 return default ;
340360 }
341361
342- if ( TryGet ( key , out TValue value ) )
362+ _lock . EnterUpgradeableReadLock ( ) ;
363+ try
343364 {
344- return value ;
345- }
365+ if ( TryGetUnlocked ( key , out TValue value ) )
366+ {
367+ return value ;
368+ }
346369
347- Func < TKey , TValue > actualFactory = factory ?? _options . Loader ;
348- if ( actualFactory == null )
349- {
350- return default ;
351- }
370+ _lock . EnterWriteLock ( ) ;
371+ try
372+ {
373+ if ( TryGetUnlocked ( key , out value ) )
374+ {
375+ return value ;
376+ }
352377
353- TValue newValue ;
354- try
355- {
356- newValue = actualFactory ( key ) ;
357- RecordLoad ( ) ;
378+ Func < TKey , TValue > actualFactory = factory ?? _options . Loader ;
379+ if ( actualFactory == null )
380+ {
381+ return default ;
382+ }
383+
384+ TValue newValue ;
385+ try
386+ {
387+ newValue = actualFactory ( key ) ;
388+ RecordLoad ( ) ;
389+ }
390+ catch
391+ {
392+ return default ;
393+ }
394+
395+ SetUnlocked ( key , newValue ) ;
396+ return newValue ;
397+ }
398+ finally
399+ {
400+ _lock . ExitWriteLock ( ) ;
401+ }
358402 }
359- catch
403+ finally
360404 {
361- return default ;
405+ _lock . ExitUpgradeableReadLock ( ) ;
362406 }
363-
364- Set ( key , newValue ) ;
365- return newValue ;
366407 }
367408
368409 /// <summary>
@@ -388,15 +429,18 @@ public void Set(TKey key, TValue value, float? ttlSeconds)
388429 return ;
389430 }
390431
391- #if ! SINGLE_THREADED
392- lock ( _evictionLock )
393- #endif
432+ _lock . EnterWriteLock ( ) ;
433+ try
434+ {
435+ SetUnlocked ( key , value , ttlSeconds ) ;
436+ }
437+ finally
394438 {
395- SetInternal ( key , value , ttlSeconds ) ;
439+ _lock . ExitWriteLock ( ) ;
396440 }
397441 }
398442
399- private void SetInternal ( TKey key , TValue value , float ? ttlSeconds )
443+ private void SetUnlocked ( TKey key , TValue value , float ? ttlSeconds = null )
400444 {
401445 float currentTime = _timeProvider ( ) ;
402446
@@ -442,6 +486,7 @@ private void SetInternal(TKey key, TValue value, float? ttlSeconds)
442486 Grow ( ) ;
443487 break ;
444488 }
489+
445490 EvictOne ( EvictionReason . Capacity ) ;
446491 }
447492
@@ -508,26 +553,29 @@ public bool TryRemove(TKey key)
508553 /// <returns>True if the entry was removed, false if not found.</returns>
509554 public bool TryRemove ( TKey key , out TValue value )
510555 {
511- value = default ;
512-
513556 if ( key == null || _disposed )
514557 {
558+ value = default ;
515559 return false ;
516560 }
517561
518- #if ! SINGLE_THREADED
519- lock ( _evictionLock )
520- #endif
562+ _lock . EnterWriteLock ( ) ;
563+ try
521564 {
522565 if ( ! _keyToIndex . TryGetValue ( key , out int index ) )
523566 {
567+ value = default ;
524568 return false ;
525569 }
526570
527571 value = _entries [ index ] . Value ;
528572 EvictEntry ( index , EvictionReason . Explicit ) ;
529573 return true ;
530574 }
575+ finally
576+ {
577+ _lock . ExitWriteLock ( ) ;
578+ }
531579 }
532580
533581 /// <summary>
@@ -540,9 +588,8 @@ public void Clear()
540588 return ;
541589 }
542590
543- #if ! SINGLE_THREADED
544- lock ( _evictionLock )
545- #endif
591+ _lock . EnterWriteLock ( ) ;
592+ try
546593 {
547594 for ( int i = 0 ; i < _entries . Length ; i ++ )
548595 {
@@ -566,6 +613,10 @@ public void Clear()
566613 InitializeFreeList ( ) ;
567614 InitializeLinkedLists ( ) ;
568615 }
616+ finally
617+ {
618+ _lock . ExitWriteLock ( ) ;
619+ }
569620 }
570621
571622 /// <summary>
@@ -580,17 +631,25 @@ public bool ContainsKey(TKey key)
580631 return false ;
581632 }
582633
583- if ( ! _keyToIndex . TryGetValue ( key , out int index ) )
634+ _lock . EnterReadLock ( ) ;
635+ try
584636 {
585- return false ;
586- }
637+ if ( ! _keyToIndex . TryGetValue ( key , out int index ) )
638+ {
639+ return false ;
640+ }
587641
588- if ( ! _entries [ index ] . IsAlive || IsExpired ( index , _timeProvider ( ) ) )
642+ if ( ! _entries [ index ] . IsAlive || IsExpired ( index , _timeProvider ( ) ) )
643+ {
644+ return false ;
645+ }
646+
647+ return true ;
648+ }
649+ finally
589650 {
590- return false ;
651+ _lock . ExitReadLock ( ) ;
591652 }
592-
593- return true ;
594653 }
595654
596655 /// <summary>
@@ -705,9 +764,8 @@ public void CleanUp()
705764 return ;
706765 }
707766
708- #if ! SINGLE_THREADED
709- lock ( _evictionLock )
710- #endif
767+ _lock . EnterWriteLock ( ) ;
768+ try
711769 {
712770 float currentTime = _timeProvider ( ) ;
713771 for ( int i = 0 ; i < _entries . Length ; i ++ )
@@ -719,6 +777,10 @@ public void CleanUp()
719777 }
720778 }
721779 }
780+ finally
781+ {
782+ _lock . ExitWriteLock ( ) ;
783+ }
722784 }
723785
724786 /// <summary>
@@ -737,16 +799,19 @@ public void Compact(float percentage)
737799 percentage = 1f ;
738800 }
739801
740- #if ! SINGLE_THREADED
741- lock ( _evictionLock )
742- #endif
802+ _lock . EnterWriteLock ( ) ;
803+ try
743804 {
744805 int toEvict = ( int ) ( _count * percentage ) ;
745806 for ( int i = 0 ; i < toEvict && _count > 0 ; i ++ )
746807 {
747808 EvictOne ( EvictionReason . Capacity ) ;
748809 }
749810 }
811+ finally
812+ {
813+ _lock . ExitWriteLock ( ) ;
814+ }
750815 }
751816
752817 /// <summary>
@@ -760,17 +825,21 @@ public void Resize(int newCapacity)
760825 return ;
761826 }
762827
763- #if ! SINGLE_THREADED
764- lock ( _evictionLock )
765- #endif
828+ _lock . EnterWriteLock ( ) ;
829+ try
766830 {
767831 while ( _count > newCapacity )
768832 {
769833 EvictOne ( EvictionReason . Capacity ) ;
770834 }
835+
771836 _capacity = newCapacity ;
772837 _protectedCapacity = ( int ) ( _capacity * _options . ProtectedRatio ) ;
773838 }
839+ finally
840+ {
841+ _lock . ExitWriteLock ( ) ;
842+ }
774843 }
775844
776845 /// <inheritdoc />
@@ -781,8 +850,10 @@ public void Dispose()
781850 return ;
782851 }
783852
784- _disposed = true ;
785853 Clear ( ) ;
854+ _disposed = true ;
855+ _lock . Dispose ( ) ;
856+ _lock = null ;
786857 }
787858
788859 private float ComputeExpirationTime (
0 commit comments