Debugging Locks and Deadlocks
Today I found myself in a problem I never guessed I would get into, deadlocks. A deadlock is a situation where one lock is waiting for another lock to clear. But the other lock can’t be cleared until the piece of code that’s currently is waiting for the lock to clear is run. More about Threads and Locks can be found here. And with a little bit of googling
Problem with deadlocks is the seem quite hard to find, and when found to solve. At the moment my program runs in a deadlock I press the Pauze button in Visual Studio, problem: Visual Studio stops at the position where the program is in the main thread. When using WPF in .NET Micro Framework this isn’t very usefull.
But I managed to find the piece of code that blocks but that doesn’t help a lot eather becouse you can’t see what piece of code currently holds the lock.
But now I think I found a solution. LockHelper. Kevin Moore had a bright idea.
When you use lock(Lock){ /* Your code */ } C# turns it into the following code:
Monitor.Enter(Lock);
try { /* Your code */ } finaly { Monitor.Exit(Lock); }
Another language statement that uses try-finaly blocks is the using keyword. This keyword is used with Disposable objects (Like GPIO Ports and Sockets) and makes sure the are Disposed after execution of the block. Example:
using(IDisposableObject) { /* Your code */ } becomes:
try { /* Your code */ } finaly { ((IDisposable) IDisposableObject).Dispose(); }
So any object declared in the using statement is certainly disposed. The idea is to create an object with an getLock method. When this method is run it enters a Monitor and returns an IDisposable object. When the Dispose method of this object is called the Monitor is exited.
With the LockHelper class you can use a lock like this:
public static LockHelper ExampleLock = new LockHelper(“Example”);
using (ExampleLock.getLock(“UniqueKey”))
{
/* User code */
}
The ”Example” string is used in Debug messages and so provides a method to identify the lock. The “UniqueKey” is stored when entering a lock. When debugging the UniqueIdentifier can be used to identify the block of code that currently holds the lock. So you should pick a unique string that is descriptive enough to find the code block.
In the debug output you can see which locks are entered and exited and how long it took to aquire a lock.
I hope it can help some tough debugging issues. I found my error before I could try this class. And a big notice that all the credit for the LockHelper goes to Kevin Moore!
Download:
You can download the LockHelper class and a sample application here: MFLockHelper.zip
Example Program:
Below you can see the code of the Demo program included in solution above:
using System; using System.Threading; using Microsoft.SPOT; using ElzeKool.Utilities; namespace DemoApplication { public class Program { public static int Cash; public static LockHelper CashLock = new LockHelper("Cash"); /// <summary> /// Thread 1, Add 100 dolar /// </summary> public static Thread DemoThread1 = new Thread(new ThreadStart(delegate() { int temp; for (int x = 0; x < 100; x++) { using (CashLock.getLock("DemoThread1")) { // Get current Cash temp = Cash; // Do some processing Thread.Sleep(100); temp += 1; // Store Cash Cash = temp; } } })); /// <summary> /// Thread 2, Add 100 dolar /// </summary> public static Thread DemoThread2 = new Thread(new ThreadStart(delegate() { int temp; for (int x = 0; x < 100; x++) { using (CashLock.getLock("DemoThread2")) { // Get current Cash temp = Cash; // Do some processing Thread.Sleep(10); temp += 1; // Store Cash Cash = temp; } } })); /// <summary> /// Thread 3, Add 100 dolar /// </summary> public static Thread DemoThread3 = new Thread(new ThreadStart(delegate() { int temp; for (int x = 0; x < 100; x++) { using (CashLock.getLock("DemoThread3")) { // Get current Cash temp = Cash; // Do some processing Thread.Sleep(10); temp += 1; // Store Cash Cash = temp; } } })); public static void Main() { // Start with no cash Cash = 0; // Start all threads DemoThread1.Start(); DemoThread2.Start(); DemoThread3.Start(); // Wait until all threads stop while (DemoThread1.IsAlive | DemoThread2.IsAlive | DemoThread3.IsAlive) { Thread.Sleep(10); } // Check Cash balance if (Cash != 300) { Debug.Print("Ow no money got lost, we have: " + Cash.ToString()); } else { Debug.Print("We got all 300 bucks, yeehaa!"); } // Wait infinite Thread.Sleep(Timeout.Infinite); } } }
The LockHelper is so simple and genius idea, great article, thx!
The idea is not mine, I only found it
But you are right it was a clever idea !