in ClrMemDiag/Desktop/lockinspection.cs [157:341]
private void SetThreadWaiters()
{
HashSet<string> eventTypes = null;
List<BlockingObject> blobjs = new List<BlockingObject>();
foreach (DesktopThread thread in _runtime.Threads)
{
int max = thread.StackTrace.Count;
if (max > 10)
max = 10;
blobjs.Clear();
for (int i = 0; i < max; ++i)
{
DesktopBlockingObject blockingObj = null;
ClrMethod method = thread.StackTrace[i].Method;
if (method == null)
continue;
ClrType type = method.Type;
if (type == null)
continue;
switch (method.Name)
{
case "AcquireWriterLockInternal":
case "FCallUpgradeToWriterLock":
case "UpgradeToWriterLock":
case "AcquireReaderLockInternal":
case "AcquireReaderLock":
if (type.Name == "System.Threading.ReaderWriterLock")
{
blockingObj = FindLocks(thread.StackLimit, thread.StackTrace[i].StackPointer, IsReaderWriterLock);
if (blockingObj == null)
blockingObj = FindLocks(thread.StackTrace[i].StackPointer, thread.StackBase, IsReaderWriterLock);
if (blockingObj != null && (blockingObj.Reason == BlockingReason.Unknown || blockingObj.Reason == BlockingReason.None))
{
// This should have already been set correctly when the BlockingObject was created. This is just a best-guess.
if (method.Name == "AcquireReaderLockInternal" || method.Name == "AcquireReaderLock")
blockingObj.Reason = BlockingReason.WriterAcquired;
else
blockingObj.Reason = BlockingReason.ReaderAcquired;
}
}
break;
case "TryEnterReadLockCore":
case "TryEnterReadLock":
case "TryEnterUpgradeableReadLock":
case "TryEnterUpgradeableReadLockCore":
case "TryEnterWriteLock":
case "TryEnterWriteLockCore":
if (type.Name == "System.Threading.ReaderWriterLockSlim")
{
blockingObj = FindLocks(thread.StackLimit, thread.StackTrace[i].StackPointer, IsReaderWriterSlim);
if (blockingObj == null)
blockingObj = FindLocks(thread.StackTrace[i].StackPointer, thread.StackBase, IsReaderWriterSlim);
if (blockingObj != null && (blockingObj.Reason == BlockingReason.Unknown || blockingObj.Reason == BlockingReason.None))
{
// This should have already been set correctly when the BlockingObject was created. This is just a best-guess.
if (method.Name == "TryEnterWriteLock" || method.Name == "TryEnterWriteLockCore")
blockingObj.Reason = BlockingReason.ReaderAcquired;
else
blockingObj.Reason = BlockingReason.WriterAcquired;
}
}
break;
case "JoinInternal":
case "Join":
if (type.Name == "System.Threading.Thread")
{
ulong threadAddr;
ClrThread target;
if (FindThread(thread.StackLimit, thread.StackTrace[i].StackPointer, out threadAddr, out target) ||
FindThread(thread.StackTrace[i].StackPointer, thread.StackBase, out threadAddr, out target))
{
if (!_joinLocks.TryGetValue(target, out blockingObj))
_joinLocks[target] = blockingObj = new DesktopBlockingObject(threadAddr, true, 0, target, BlockingReason.ThreadJoin);
}
}
break;
case "Wait":
case "ObjWait":
if (type.Name == "System.Threading.Monitor")
{
blockingObj = FindMonitor(thread.StackLimit, thread.StackTrace[i].StackPointer);
if (blockingObj == null)
blockingObj = FindMonitor(thread.StackTrace[i].StackPointer, thread.StackBase);
blockingObj.Reason = BlockingReason.MonitorWait;
}
break;
case "WaitAny":
case "WaitAll":
if (type.Name == "System.Threading.WaitHandle")
{
ulong obj = FindWaitObjects(thread.StackLimit, thread.StackTrace[i].StackPointer, "System.Threading.WaitHandle[]");
if (obj == 0)
obj = FindWaitObjects(thread.StackTrace[i].StackPointer, thread.StackBase, "System.Threading.WaitHandle[]");
if (obj != 0)
{
BlockingReason reason = method.Name == "WaitAny" ? BlockingReason.WaitAny : BlockingReason.WaitAll;
if (!_waitLocks.TryGetValue(obj, out blockingObj))
_waitLocks[obj] = blockingObj = new DesktopBlockingObject(obj, true, 0, null, reason);
}
}
break;
case "WaitOne":
case "InternalWaitOne":
case "WaitOneNative":
if (type.Name == "System.Threading.WaitHandle")
{
if (eventTypes == null)
{
eventTypes = new HashSet<string>();
eventTypes.Add("System.Threading.Mutex");
eventTypes.Add("System.Threading.Semaphore");
eventTypes.Add("System.Threading.ManualResetEvent");
eventTypes.Add("System.Threading.AutoResetEvent");
eventTypes.Add("System.Threading.WaitHandle");
eventTypes.Add("Microsoft.Win32.SafeHandles.SafeWaitHandle");
}
ulong obj = FindWaitHandle(thread.StackLimit, thread.StackTrace[i].StackPointer, eventTypes);
if (obj == 0)
obj = FindWaitHandle(thread.StackTrace[i].StackPointer, thread.StackBase, eventTypes);
if (obj != 0)
{
if (_waitLocks == null)
_waitLocks = new Dictionary<ulong, DesktopBlockingObject>();
if (!_waitLocks.TryGetValue(obj, out blockingObj))
_waitLocks[obj] = blockingObj = new DesktopBlockingObject(obj, true, 0, null, BlockingReason.WaitOne);
}
}
break;
case "TryEnter":
case "ReliableEnterTimeout":
case "TryEnterTimeout":
case "Enter":
if (type.Name == "System.Threading.Monitor")
{
blockingObj = FindMonitor(thread.StackLimit, thread.StackTrace[i].StackPointer);
if (blockingObj != null)
blockingObj.Reason = BlockingReason.Monitor;
}
break;
}
if (blockingObj != null)
{
bool alreadyEncountered = false;
foreach (var blobj in blobjs)
{
if (blobj.Object == blockingObj.Object)
{
alreadyEncountered = true;
break;
}
}
if (!alreadyEncountered)
blobjs.Add(blockingObj);
}
}
foreach (DesktopBlockingObject blobj in blobjs)
blobj.AddWaiter(thread);
thread.SetBlockingObjects(blobjs.ToArray());
}
}