Click here to Skip to main content
Click here to Skip to main content

Deadlock Detection in Existing Code

By , 11 Jan 2008
 

Introduction

Deadlocks are common problems in multi-threaded programming. When it comes to multithreading development, the most common problem developers are facing is critical sections. It is not uncommon to use more than a single lock, but if one does not pay attention to the order of the locks, or to the context in which they are being called (e.g., from within a callback), deadlocks will form. (There are many reasons for deadlocks to occur other than the obvious critical section, e.g., two threads that are waiting for each other to signal an event, but we will not discuss them here).

As with anything that is related to threads, timing is everything. The most problematic deadlocks are those which occur rarely, they have this amazing nature of occurring at your client's site...

What if we could make the rare case the normal case? Recall that the reason a deadlock does not occur has to do with the fact that two threads that might deadlock happen not to be at the problematic places at the same time. So, all we have to do is record their "visits" at the problematic places, then we need to verify that the locks order is always the same, and if not, output the stack trace and notify the developer that we found a mismatch in the locks order.

The attached ZIP file contains a DLL that does exactly that. The DLL hacks all common (Enter, Exit, TryEnter methods, but it can be extended easily to support others, if used) monitor calls (including, of course, the .NET lock keyword) and keeps track of the locks order. Once it finds a problem in the order, it creates two stack traces, and directs you to a sample of the problematic locks (after fixing the error, repeat the test and see if there is another problem with the detected locks in another flow).

Note that there is no need for the deadlock to really occur; rather, it is only important that suspected flows (or all flows) will be performed at least once.

Using the code

  • Add the file incslock.cs to your project
  • Add a reference to the slockimp.dll
  • Compile your component and execute it

Analyzing the stacks (slockimp based)

  1. Once a problem is detected, the console (if one exists) will output the last lock conflicts with the nth of the stack
  2. Two files are being created in the working directory: first_xxx.txt and now_yyy (xxx and yyy represent numbers)
  3. Go to the "now" file – find the last lock (prior to the last four calls that are inner to the DLL)
  4. This is the lock that when locked caused the problem

  5. In order to find the other problematic lock, you can either:
    1. Spot the nth lock from the beginning of the stack (not counting locks that were recursively locked, and locks that were locked and released)
    2. Find the last lock in the "first" file
  6. Go over the "first" file stack, and find the place in which the lock from 3.a was locked

This is the second version of the implementation, which now supports more complex scenarios like the dining philosophers (thanks to Sergey's question below). The stack files are numbered as follows: 0_xxx.txt, 1_xxx.txt, and so on...

0_xxx points to the lock that when locked caused the problem. Other files point to other locks that created a kind of circular waiting.

Points of interest

Notice that you do not need to change a single existing line of code. Rather, you add two files to your project. The trick here is to cause the compiler to use our reference for implementing monitor calls rather then .NET's. (In the DLL itself, the locks are being locked and released properly, so your program should work fine critical-section wise). This trick is similar to replacing a header file in C.

Notice that the DLL is not meant for production, since it affects performance. Also, notice that the DLL allows recursive locks of the same lock. The DLL, however, will notify about possible deadlocks even if the waiting time is not infinite. (Despite the fact this is a false alarm, it indicates bad behavior, since such a behavior might influence performance and will deadlock if the time would be set to infinite).

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

eransha
Team Leader
Israel Israel
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
Generalslockimp.dllmemberComplexityChaos19 Nov '09 - 9:03 
Where's the source?
Questionno stack trace files are being generatedmemberPMD17 Jun '09 - 4:48 
Hi. I just tried using this yesterday on an application that sporadically deadlocks. I added incslock.cs to my project and referenced the dll. I compiled the project and ran it. I did not get any stack trace files in the working directory.
 
Since I was not sure if the trace files would be output only when a potential / actual deadlock scenario is detected, I created a new project that definitely causes a deadlock. I included the files to this project and ran it. Still I dont get any trace files.
 
I guess I must be missing something basic here since its working for everyone else.
 
The code to simulate the deadlock is below (in case its relevant).
 
I would appreciate any help.
 
Thanks!
 
--------------------------------------------------------------------------------------
 
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
 
namespace CreatADeadlock
{
public partial class Deadlock : Form
{
private object _lockObject1 = new object();
private object _lockObject2 = new object();
 
public Deadlock()
{
InitializeComponent();
}
 
private void btnCreateDeadlock_Click(object sender, EventArgs e)
{
Thread th1 = new Thread(Thread1Execution);
th1.Name = "Thread 1";
th1.Start();
 
MainThreadExecution();
}
 

private void Thread1Execution()
{
lock (_lockObject1)
{
Thread.Sleep(10 * 1000);
 
lock (_lockObject2)
{
;
}
 
}
 
}
 
private void MainThreadExecution()
{
lock (_lockObject2)
{
Thread.Sleep(10 * 1000);
 
lock (_lockObject1)
{
;
}
 
}
 
}
}
}
AnswerRe: no stack trace files are being generatedmembereransha17 Jun '09 - 9:43 
Let's start with why your example doesn't produce traces - the reason is that you create a 100% deadlock...
You can read the explanation of why one cannot detect a 100% deadlock either in the article or in my response to the first post.
 
Anyway according to the fact use System.Collections.Generic I assume you are trying to work with .Net 2.0. As I wrote as response to another post, this demands some workaround since .Net 2 doesn't allow the trick of overriding the default system class... Read my response to the post about .Net 2.0 and you will be able to detect your deadlocks...
 
If you need any further assistance let me know.
GeneralHello Eranmemberevytre201 Apr '09 - 11:01 
I'm programming in native c++. (VS6)
I'm having a dead lock problems too and i don't have a clue where to start looking.
I want to use your idea to solve my problem, and rewrite it in native c++.
Can you upload the complete source code.
 
Or contact me on the mail: evyatarap@gmail.com.
 
Thanks in advanced,
Evyatar.
GeneralRe: Hello Eranmembereransha17 Jun '09 - 9:46 
Evyatar,
 
Sorry for the late response. You can use .Net reflector to reverse engineer the dll.
GeneralSimple enhancement to imitate lock() statementmembercwienands15 Feb '09 - 17:45 
I enhanced the code a little bit to facilitate the switch-over of lock() blocks. Each 'lock(obj)' line is replaced with 'using (Monitor.Lock(obj))'. The 'using' statement ensures that the Monitor is properly exited no matter whether an exception is thrown or not, thereby keeping the semantics of the 'lock' block. If you have a regex-capable replacement tool, you should be done in no time.
 
public static IDisposable Lock(object obj)
{
	return new LockContainer(obj);
}
 
class LockContainer : IDisposable
{
	object m_Object;
 
	public LockContainer(object obj)
	{
		m_Object = obj;
		if (obj != null) slockimp.imp.DoLock(obj);
	}
 
	public void Dispose()
	{
		if (m_Object != null) slockimp.imp.DoUnlock(m_Object);
	}
}
 
Great work. I'm keeping my fingers crossed your DLL will help me finding a tricky deadlock situation.
 
Christoph
GeneralRe: Simple enhancement to imitate lock() statementmembereransha23 Feb '09 - 8:53 
Good luck! This is a similar pattern to the timedlock (See my answer regarding 2005).
Generalwhat's the "trick"memberjdstuart25 Nov '08 - 22:09 
I tried to compile the code with Visual Studio 2008, but kept on getting the warning, stating that the compiler rather used the references from mscorlib. I've read up on it, and it seems that you have to compile your application with /nostdlib. How can I get the compiler to compile using /nostdlib in a web application?
GeneralRe: what's the "trick"membereransha29 Nov '08 - 3:40 
without using the standard libraries you need to define some basic classes as System.Object by yourself. This /nostdlib flag tells the compiler not to reference mscorlib.
Since I haven't found an easy way to tell the compiler to prefer my version of System.Threading.Monitor, I would use the search/replace I suggested in one of the threads below.
GeneralCant downloadmembernaveed20 Dec '07 - 11:21 
The links to download the zip files don't work.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 11 Jan 2008
Article Copyright 2007 by eransha
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid