@@ -1289,7 +1289,7 @@ private static void AddWrapperToReferenceTrackerHandleCache(NativeObjectWrapper
12891289
12901290        private sealed class  RcwCache
12911291        { 
1292-             private readonly Lock  _lock =  new  Lock ( useTrivialWaits :  true ) ; 
1292+             private readonly ReaderWriterLockSlim  _lock =  new  ReaderWriterLockSlim ( ) ; 
12931293            private readonly Dictionary< IntPtr,  GCHandle >  _cache  =  [ ] ; 
12941294
12951295            /// <summary> 
@@ -1301,7 +1301,8 @@ private sealed class RcwCache
13011301            /// <returns>The proxy object currently in the cache for <paramref name="comPointer"/> or the proxy object owned by <paramref name="wrapper"/> if no entry exists and the corresponding native wrapper.</returns> 
13021302            public ( NativeObjectWrapper  actualWrapper ,  object  actualProxy )  GetOrAddProxyForComInstance ( IntPtr  comPointer ,  NativeObjectWrapper  wrapper ,  object  comProxy ) 
13031303            { 
1304-                 lock  ( _lock ) 
1304+                 _lock . EnterWriteLock ( ) ; 
1305+                 try 
13051306                { 
13061307                    Debug . Assert ( wrapper . ProxyHandle . Target  ==  comProxy ) ; 
13071308                    ref  GCHandle rcwEntry =  ref  CollectionsMarshal. GetValueRefOrAddDefault( _cache,  comPointer ,  out  bool  exists) ; 
@@ -1336,32 +1337,63 @@ private sealed class RcwCache
13361337                    // Return our target object. 
13371338                    return  ( wrapper,  comProxy) ; 
13381339                } 
1340+                 finally 
1341+                 { 
1342+                     _lock. ExitWriteLock( ) ; 
1343+                 } 
13391344            } 
13401345
13411346            public object ?  FindProxyForComInstance( IntPtr comPointer) 
13421347            { 
1343-                 lock  ( _lock) 
1348+                 _lock. EnterReadLock( ) ; 
1349+                 try 
13441350                { 
1345-                     if  ( _cache . TryGetValue ( comPointer ,  out  GCHandle  existingHandle) ) 
1351+                     if  ( ! _cache. TryGetValue( comPointer,  out  GCHandle existingHandle) ) 
13461352                    { 
1347-                         if  ( existingHandle . Target  is  NativeObjectWrapper {  ProxyHandle. Target:  object  cachedProxy } ) 
1348-                         { 
1349-                             // The target exists and is still alive. Return it. 
1350-                             return  cachedProxy; 
1351-                         } 
1353+                         // No entry in the cache. 
1354+                         return  null ; 
1355+                     } 
1356+                     if  ( existingHandle. Target is  NativeObjectWrapper {  ProxyHandle. Target:  object  cachedProxy } ) 
1357+                     { 
1358+                         // The target exists and is still alive. Return it. 
1359+                         return  cachedProxy; 
1360+                     } 
1361+                     // The target was collected, so we need to remove the entry from the cache. 
1362+                     // We'll do this in a write lock after we exit the read lock. 
1363+                     // We don't use an upgradeable lock here as only one thread can hold an upgradeable lock at a time, 
1364+                     // effectively eliminating the benefit of using a reader-writer lock. 
1365+                 } 
1366+                 finally 
1367+                 { 
1368+                     _lock. ExitReadLock( ) ; 
1369+                 } 
13521370
1353-                         // The target was collected, so we need to remove the entry from the cache. 
1371+                 _lock . EnterWriteLock ( ) ; 
1372+                 try 
1373+                 { 
1374+                     // Someone else could have removed the entry or added a new one in the time 
1375+                     // between us releasing the read lock and acquiring the write lock. 
1376+                     if  ( _cache . TryGetValue ( comPointer ,  out  GCHandle  existingHandle ) 
1377+                         &&  existingHandle . Target  is  null ) 
1378+                     { 
1379+                         // There's still a dead entry in the cache, 
1380+                         // remove it. 
13541381                        _cache . Remove ( comPointer ) ; 
13551382                        existingHandle . Free ( ) ; 
13561383                    } 
1357- 
1358-                     return  null ; 
13591384                } 
1385+                 finally 
1386+                 { 
1387+                     _lock . ExitWriteLock ( ) ; 
1388+                 } 
1389+ 
1390+                 return  null ; 
13601391            } 
13611392
13621393            public  void  Remove ( IntPtr  comPointer ,  NativeObjectWrapper  wrapper ) 
13631394            { 
1364-                 lock  ( _lock ) 
1395+                 _lock . EnterWriteLock ( ) ; 
1396+                 try 
13651397                { 
13661398                    // TryGetOrCreateObjectForComInstanceInternal may have put a new entry into the cache 
13671399                    // in the time between the GC cleared the contents of the GC handle but before the 
@@ -1376,6 +1408,10 @@ public void Remove(IntPtr comPointer, NativeObjectWrapper wrapper)
13761408                        cachedRef . Free ( ) ; 
13771409                    } 
13781410                } 
1411+                 finally 
1412+                 { 
1413+                     _lock . ExitWriteLock ( ) ; 
1414+                 } 
13791415            } 
13801416        } 
13811417
0 commit comments