/// <summary>Creates a textual description of the deadlock.</summary> /// <param name="currentChain">The deadlock cycle.</param> /// <param name="locksHeldByThreads">The table containing what locks are held by each thread holding locks.</param> /// <returns>The description of the deadlock.</returns> private static string CreateDeadlockDescription( CycleComponentNode currentChain, Dictionary <Thread, List <MonitorState> > locksHeldByThreads) { StringBuilder desc = new StringBuilder(); for (CycleComponentNode node = currentChain; node != null; node = node.Next) { desc.AppendFormat("Thread {0} waiting on {1} ({2:X}) while holding ", node.Thread.ManagedThreadId, node.MonitorState.MonitorObject.ToString(), RuntimeHelpers.GetHashCode(node.MonitorState.MonitorObject)); bool needsComma = false; foreach (MonitorState ms in locksHeldByThreads[node.Thread]) { if (needsComma) { desc.Append(", "); } desc.AppendFormat("{0} ({1:X})", ms.MonitorObject.ToString(), RuntimeHelpers.GetHashCode(ms.MonitorObject)); needsComma = true; } desc.AppendLine(); } return(desc.ToString()); }
public bool ContainsThread(Thread t) { for (CycleComponentNode node = this; node != null; node = node.Next) { if (node.Thread == t) { return(true); } } return(false); }
/// <summary>Creates a textual description of the deadlock.</summary> /// <param name="currentChain">The deadlock cycle.</param> /// <param name="locksHeldByThreads">The table containing what locks are held by each thread holding locks.</param> /// <returns>The description of the deadlock.</returns> private static string CreateDeadlockDescription( MonitorState competedState, CycleComponentNode currentChain, Dictionary <Thread, List <MonitorState> > locksHeldByThreads) { StringBuilder desc = new StringBuilder(); for (CycleComponentNode node = currentChain; node != null; node = node.Next) { desc.AppendFormat("\r\nThread {0} waiting on: {1} ({2:X})\r\nCalling from:\r\n{3}\nwhile holding:\r\n", node.Thread.ManagedThreadId, node.MonitorState.MonitorObject.ToString(), RuntimeHelpers.GetHashCode(node.MonitorState.MonitorObject), node.MonitorState.traces[node.Thread]); foreach (MonitorState ms in locksHeldByThreads[node.Thread]) { desc.AppendFormat("{0} ({1:X})\r\n{2}", ms.MonitorObject.ToString(), RuntimeHelpers.GetHashCode(ms.MonitorObject), ms.traces[node.Thread]); } desc.AppendLine(); } return(desc.ToString()); }
public CycleComponentNode(Thread thread, MonitorState ms, CycleComponentNode next) { Thread = thread; MonitorState = ms; Next = next; }
/// <summary>Creates a textual description of the deadlock.</summary> /// <param name="currentChain">The deadlock cycle.</param> /// <param name="locksHeldByThreads">The table containing what locks are held by each thread holding locks.</param> /// <returns>The description of the deadlock.</returns> private static string CreateDeadlockDescription( MonitorState competedState, CycleComponentNode currentChain, Dictionary<Thread, List<MonitorState>> locksHeldByThreads) { StringBuilder desc = new StringBuilder(); for (CycleComponentNode node = currentChain; node != null; node = node.Next) { desc.AppendFormat("\r\nThread {0} waiting on: {1} ({2:X})\r\nCalling from:\r\n{3}\nwhile holding:\r\n", node.Thread.ManagedThreadId, node.MonitorState.MonitorObject.ToString(), RuntimeHelpers.GetHashCode(node.MonitorState.MonitorObject), node.MonitorState.traces[node.Thread]); foreach (MonitorState ms in locksHeldByThreads[node.Thread]) desc.AppendFormat("{0} ({1:X})\r\n{2}", ms.MonitorObject.ToString(), RuntimeHelpers.GetHashCode(ms.MonitorObject), ms.traces[node.Thread]); desc.AppendLine(); } return desc.ToString(); }
/// <summary>Creates a textual description of the deadlock.</summary> /// <param name="currentChain">The deadlock cycle.</param> /// <param name="locksHeldByThreads">The table containing what locks are held by each thread holding locks.</param> /// <returns>The description of the deadlock.</returns> private static string CreateDeadlockDescription( CycleComponentNode currentChain, Dictionary<Thread, List<MonitorState>> locksHeldByThreads) { StringBuilder desc = new StringBuilder(); for (CycleComponentNode node = currentChain; node != null; node = node.Next) { desc.AppendFormat("Thread {0} waiting on {1} ({2:X}) while holding ", node.Thread.ManagedThreadId, node.MonitorState.MonitorObject.ToString(), RuntimeHelpers.GetHashCode(node.MonitorState.MonitorObject)); bool needsComma = false; foreach (MonitorState ms in locksHeldByThreads[node.Thread]) { if (needsComma) desc.Append(", "); desc.AppendFormat("{0} ({1:X})", ms.MonitorObject.ToString(), RuntimeHelpers.GetHashCode(ms.MonitorObject)); needsComma = true; } desc.AppendLine(); } return desc.ToString(); }
/// <summary>Throws an exception if a deadlock would be caused by the current thread waiting on the specified lock.</summary> /// <param name="targetMs">The target lock data.</param> private static void ThrowIfDeadlockDetected(MonitorState targetMs) { // If no thread is holding the target lock, then this won't deadlock... if (targetMs.OwningThread == null) { return; } // For the deadlock detection algorithm, we need to know what locks are // currently held by which threads as well as which threads are waiting on // which locks. We already have this information, but we need it in a tabular // form for easier use and better perf. Dictionary <Thread, List <MonitorState> > locksHeldByThreads; Dictionary <MonitorState, List <Thread> > threadsWaitingOnLocks; CreateThreadAndLockTables(out locksHeldByThreads, out threadsWaitingOnLocks); // As we iterate over the wait graph, we'll need to store the list of threads still left to examine Queue <CycleComponentNode> threadsToFollow = new Queue <CycleComponentNode>(locksHeldByThreads.Count); // But rather than just storing the thread, we also store the threads in the cycle that got us to this thread. // The top of the stack is the actual thread to be examined. threadsToFollow.Enqueue(new CycleComponentNode(Thread.CurrentThread, targetMs, null)); while (threadsToFollow.Count > 0) { // Get the next thread to examine CycleComponentNode currentChain = threadsToFollow.Dequeue(); Thread currentThread = currentChain.Thread; // If this thread doesn't hold any locks, no point in examining it List <MonitorState> locksHeldByThread; if (!locksHeldByThreads.TryGetValue(currentThread, out locksHeldByThread)) { continue; } // For each lock it does hold, add to the thread examination list all threads // waiting on it. And for each, see if it completes a cycle that results in // a deadlock. foreach (MonitorState ms in locksHeldByThread) { List <Thread> nextThreads; if (!threadsWaitingOnLocks.TryGetValue(ms, out nextThreads)) { continue; } foreach (Thread nextThread in nextThreads) { // If any thread waiting on this lock is in the current stack, // it's completng a cycle... deadlock! if (currentChain.ContainsThread(nextThread)) { throw new SynchronizationLockException( CreateDeadlockDescription(targetMs, currentChain, locksHeldByThreads)); } // Clone the stack of threads in the possible cycle and add this to the top, // then queue the stack for examination. threadsToFollow.Enqueue(new CycleComponentNode(nextThread, ms, currentChain)); } } } }