Skip to content

Commit b28b882

Browse files
Bring back the reader-writer lock for RcwCache (#120675)
Co-authored-by: Copilot <[email protected]>
1 parent caeb3b4 commit b28b882

File tree

1 file changed

+49
-13
lines changed
  • src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices

1 file changed

+49
-13
lines changed

src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/ComWrappers.cs

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)