This appears to be a bug and the .NET team is tracking it. It's considered a relative corner case so it may not be a high priority fix.
Here's some research that Vladimir did on this. <snip> There is a bug/'deadlock issue'. There is a mechanism to release the lock if an exception is thrown inside a synchronized method, so if the commented out code below in 'foo' is used instead of the current body of 'foo' there is no deadlock. But if you do modify the 'this' pointer the second thread deadlocks, regardless of if 'foo' throws an exception or not. //////////////////////////// .assembly extern mscorlib {} .assembly test {} .class private auto ansi beforefieldinit Test extends [mscorlib]System.Object { .method public hidebysig newslot virtual instance void foo() cil managed synchronized { ldnull starg 0 //newobj instance void ['mscorlib']System.Exception::.ctor() //throw //ldstr "After throw" //call void ['mscorlib']System.Console::WriteLine(string) ret } .method private hidebysig static void Main() cil managed { .entrypoint .locals init (class Test V_1, class [mscorlib]System.Threading.Thread V_2) .try { newobj instance void Test::.ctor() stloc.0 ldloc.0 callvirt instance void Test::foo() leave.s SecondCall } catch ['mscorlib']System.Exception { pop ldstr "Inside catch" call void ['mscorlib']System.Console::WriteLine(string) leave.s SecondCall } // end handler SecondCall: .try { ldloc.0 ldftn instance void Test::foo() newobj instance void [mscorlib]System.Threading.ThreadStart::.ctor(object, native int) newobj instance void [mscorlib]System.Threading.Thread::.ctor(class [mscorlib]System.Threading.ThreadStart) stloc.1 ldloc.1 callvirt instance void [mscorlib]System.Threading.Thread::Start() ldloc.1 callvirt instance void [mscorlib]System.Threading.Thread::Join() leave.s EndIL } catch ['mscorlib']System.Exception { pop ldstr "Inside catch2" call void ['mscorlib']System.Console::WriteLine(string) leave.s EndIL } // end handler EndIL: ret } .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ldarg.0 call instance void [mscorlib]System.Object::.ctor() ret } } //////////////////////////// </snip> This posting is provided "AS IS" with no warranties, and confers no rights. -----Original Message----- From: Jeroen Frijters [mailto:[EMAIL PROTECTED]] Sent: Wednesday, June 12, 2002 9:29 AM To: [EMAIL PROTECTED] Subject: Bug? The code below throws an exception: Unhandled Exception: System.ArgumentNullException: Value cannot be null. at System.Threading.Monitor.Exit(Object obj) at Test.foo() at Test.Main() Is this correct behavior? I understand why it is happening, but from my reading of the spec, this behavior is incorrect. BTW, the commercial implementation behaves the same (although the Monitor.Exit line is missing from the stack trace, so I guess it inlines that). Regards, Jeroen //////////////////// start ///////////////////// .assembly extern mscorlib {} .assembly test {} .class private auto ansi beforefieldinit Test extends [mscorlib]System.Object { .method public hidebysig newslot virtual instance void foo() cil managed synchronized { ldnull starg 0 ret } .method private hidebysig static void Main() cil managed { .entrypoint newobj instance void Test::.ctor() callvirt instance void Test::foo() ret } .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ldarg.0 call instance void [mscorlib]System.Object::.ctor() ret } } //////////////////// end /////////////////////