Going beyond Monitor.Enter/Exit (C# lock) with LockHelper
Sample code: LockHelper.cs [6KB]
Debugging threading issues is painful.
Really painful.
One of the most painful problems: dealing with deadlocks--waiting forever on a Monitor.Enter (C# lock) and not knowing which thread already has the lock.
I tried to dig in on this on the CLR Base Class Library Forum. I was hopeful, since Java has a method [Thread.holdsLock(obj)] that returns the holder of the current lock
Sadly, .NET lacks this functionality.
Enter a can-do attitude: why not build it myself.
Enter LockHelper.
Goals:
- Identical semantics to Monitor.Enter/Exit
- Enable other Monitor features (Pulse, Wait)
- Close to identical syntax to C# lock
- Ability to know, given an instance of LockHelper, the current owning thread
- Other conveniences (CheckAccess and VerifyAccess)
Wrapping Monitor.Enter/Exit isn't that hard. As you see with the implementation, it's pretty easy to leverage IDisposable + the C# using statement to get syntax that is close to lock.
Instead of:
public void Foo()
{
lock(m_lockObject)
{
//do stuff
}
}
private readonly object m_lockObject = new object();
Do this:
public void Foo()
{
using(m_lockObject.GetLock())
{
//do stuff
}
}
private readonly LockHelper m_lockObject = new LockHelper("foo");
GetLock() returns a private class that implements IDisposable. The using block has identical try/finally semantics to the lock keyword. (See this article in the C# Programming Guide for details.)
I had to be religious about storing away Thread.Name for the current thread at all times. While debugging, I found that VS was often unable to evaluate the property of the owning thread.
So now I have enough plumbing to figure out which thread has a lock on my lock object. Cool.
What else?
Exposing Pulse and Wait was pretty easy. Beyond these basics, I stole the CheckAccess/VerifyAccess ideas from WPF.
Often times one will have private helpers in a class that access/modify the state of a class. Often times these methods (should) run under the assumption that they were called in the context of a lock.
The simple way around this is to re-lock the object inside the helper method, but this can be a source of deadlocks and really doesn't help maintain the implementation contract.
What one wants: a way to assert that the calling thread owns a lock.
Done!
CheckAccess returns true if the current thread has the lock. Otherwise, false.
VerifyAccess goes a step further and throws an exception if the calling thread doesn't own the lock.
Pretty cool, huh?
Note: I was not on the CLR team at Microsoft. I was not a developer at Microsoft, just a lowly PM. I'm pretty confident this works. At least I haven't hit any issues with it. Please be careful throwing this in your own projects. This is certainly out of the realm of advertised best-practices from MS.
It works for me, though.
Let me know what you think.
Happy hacking!