Lock(object) and ThreadAbortException
We have experience some weird deadlock using managed code.
We have a class with some shared resources. To protect the shared resources, we create a synchronization object, and use Lock(object) prior to access the shared resources. There is no nested lock so in theory we should never have deadlock.
However, for some reason, one of the call has stucked in the Lock(object) call.
After a round of discussion with CLR team, we finally figure out the problem.
It is a debug build. (not surprising given we are in development.) The C# compiler generates the following code for Lock(object) in debug mode.
IL_0039: ldarg.0
IL_003a: ldfld stateLock
IL_003f: dup
IL_0040: stloc.3
IL_0041: call System.Threading.Monitor::Enter
IL_0046: nop
.try
{
...
} // end .try
.finally
{
IL_005d: ldloc.3
IL_005e: call System.Threading.Monitor::Exit
IL_0063: nop
IL_0064: endfinally
} // end .finally
You see, it inserts an extra nop between Monitor.Enter and try/finally. Right at the nop instruction, a ThreadAbortException is thrown in this thread, leaving the lock to be orphaned. (The exact reason why the exception is thrown is unknown. It is definitely not thrown by our code.)
Joe Duffy from CLR team has discussed the exact symptom in his blog
https://www.bluebytesoftware.com/blog/PermaLink,guid,d9ff204a-a8a5-400e-bcbc-dedb90a7d11a.aspx
Writing managed code does give you more challenge in writing reliable product.
Comments
Anonymous
July 30, 2007
I wouldn't say managed code makes it more challenging to write a reliable product - it just gives you a different set of reliability problems to worry about. Reliability is always a challenge, no matter what language you work with.Anonymous
July 31, 2007
Do you have a solution for that problem yet? It appears to me that it must be a common major slip in 'using' and 'lock' statement implementation. BTW, there is a way to amortise the trouble, by wrapping Monitor's functionality in CriticalFinalizer object. Even if async exception happens, the lock will be released sometimes latter. But it is not the true solution in general case. I think Microsoft should address async exception problem with something substantial.