CSLint: deadlock detector for .Net

Introduction

CSLint is add-in for .Reflector allowing you to detect possible deadlocks in your .Net application. CSLint doesn't need source code of your application - it works with assemblies. CSLint parse method bodies and locates lock statements and method calls. It builds call graph uses it to construct resource graph. If there is a loop in resource graph then deadlock may happen if method contains this lock statements are executed concurrently.

Lets look at the simple example:

        
class DeadlockProneClass
{
    object monitor1 = new object();
    object monitor2 = new object();

    public void method1()
    {
        lock (monitor1)
        {
            lock (monitor2)
            {
                // do something
            }
        }
    }

    public void method2()
    {
        lock (monitor2)
        {
            lock (monitor1)
            { 
                // do something
            }
        }
    }
}
If methods method1 and method2 are executed concurrently, it can happen that thread executing method1 locks monitor1 and wait for monitor2. At the same time thread executing method2 locks monitor2 and waits for monitor1. In this case these two threads blocks each other - none of them can continue execution. Such situation is called deadlock. And this is what CSLint tries to detect in your code.

The example above seems to be artificial and not frequently happen in real life: why programmer has to lock two objects and do it in different order? But it is just the trivial example of deadlock. Some method m1 may lock some resource r1 and then invoke some other method m2 of the other class. m2 can invoke m3 and so on... Some of the methods in this invocation chain can lock another resource r2. So we will have edge r1->r2 in resource graph. There can exist some other invocation chain (of completely different methods) which cause that resource r2 is locked before r1. And one again we have conditions for possible deadlock appearance. Below is the most trivial example of such situation:

class Class1
{
    public void method(Class2 c2)
    {
        lock (this)
        {
            c2.method(this);
        }
    }
}

class Class2
{
    public void method(Class1 c1)
    {
        lock (this)
        {
            c1.method(this);
        }
    }
}

Usage

It is really not so easy to analyze all possible invocations in your application and find out where deadlock is possible. CSLint can do this work for you. You only need to perform the following steps:
  1. Start Reflector
  2. Load CSLint Add-in (CSLint/CSLintAddin/bin/Debug/CSLintAddin.dll)
  3. Open assemblies of your application in Reflector
  4. In Tools menu choose "Detect Deadlocks" item
You can invoke Reflector from CSLint\CSLintAddin\bin\Debug directory - it already contains CSLint add-in loaded as well as test assembly, where you can check how detection of deadlocks works. CSLint will show message with list of all detected loops (each loop corresponds to the possible deadlock). Information about loop contains locked resources and invocation chains which connects code locking these resources. Also for your conviniece information about detected deadlocks is saved in deadlock.lst file which is create in the directpry from which you have started Reflect. Information about deadlocks loks something like this:
Loop 1: lock(Tests.Test4.a) in method Tests.Test4.f1 can cause deadlock
Loop 1: lock(Tests.Test4.b) in method Tests.Test4.f2 can cause deadlock
Loop 2: lock(Tests.Test3.B) in method Tests.Test3.B.foo can cause deadlock
  Loop 2: called from Tests.Test3.g
  Loop 2: called from Tests.Test3.A.foo
Loop 2: lock(Tests.Test3.A) in method Tests.Test3.A.foo can cause deadlock
  Loop 2: called from Tests.Test3.f
  Loop 2: called from Tests.Test3.B.foo
Loop 3: lock(Tests.Test2.a) in method Tests.Test2.f1 can cause deadlock
Loop 3: lock(Tests.Test2.b) in method Tests.Test2.f2 can cause deadlock
Loop 4: lock(Tests.Test1.Class5.monitor) in method Tests.Test1.Class5.method1 can cause deadlock
  Loop 4: called from Tests.Test1.Class4.method
Loop 4: lock(Tests.Test1.Class4.monitor) in method Tests.Test1.Class5.method2 can cause deadlock
Loop 5: lock(Tests.Test1.Class3.monitor2) in method Tests.Test1.Class3.method1 can cause deadlock
Loop 5: lock(Tests.Test1.Class3.monitor1) in method Tests.Test1.Class3.method2 can cause deadlock
Loop 6: lock(Tests.Test1.Class2) in method Tests.Test1.Class2.method can cause deadlock
  Loop 6: called from Tests.Test1.Class1.method
Loop 6: lock(Tests.Test1.Class1) in method Tests.Test1.Class1.method can cause deadlock
  Loop 6: called from Tests.Test1.Class2.method
If your application is large, CSLint analysis can take significant amount of time, please be patient.

Limitations

CSLint add-in has the following limitations:
  1. Reflector doesn't provide access to debug information, so CSLint knows nothing about source files and line number of the code. So it is not able to show you precise location where resource is locked or method is invoked.
  2. CSLint performs only static analysis. It means that if you locking field x of class Y, then CSLint assumes then everywhere in your application Y.x refers to the same object instance (so it is the single resource). Certainly it may be not true, in this case deadlock detected by CSLint will be a false alarm. Also CSLint assumes that if method m1 MAY call m2, then it always calls it. Moreover, it is clear that in code fragment:
         if (someCondition) { 
             method1();
         } else {
             method2();
         }
    
    only one of method1 and method2 can be actually invoked. But CSLint assumes that both methods are invoked.
  3. CSLint takes in account only lock constructions. It doesn't consider Monitor.Enter, ReaderWriterLock.AcquireReradeLock methods as well as any other .Net synchronization primitives.
  4. CSLint doesn't try to detect race conditions - the second main problem of multithreaded programming

Distribution terms

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the Software), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR OF THIS SOFTWARE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

I will provide e-mail support and help you with development of CSLint add-in for .Net Reflector.


Look for new version at my homepage | E-Mail me about bugs and problems