Here is the section of the standard I'm confused by: http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-335.pdf#page=178&zoom=auto,87,610%22
2.1. If the type is not yet initialized, try to take an initialization lock.
2.2.1. If not successful, see whether this thread or any thread waiting for this thread to complete already holds the lock.
2.2.2. If so, return since blocking would create a deadlock. This thread will now see an incompletely initialized state for the type, but no deadlock will arise.
The following code deadlocks when I test it, which seems to contradict the standard:
public static class Foo {
static Foo() {
var otherThread = new Thread(() => { Thread.Sleep(1000); SomeFunction(); });
otherThread.Start();
otherThread.Join();
}
public static void SomeFunction() {
}
}
class Program {
static void Main() {
Foo.SomeFunction();
}
}
According to the standard, I expect the following to happen:
What's wrong here?
"any thread waiting for this thread to complete" refers to any thread waiting using the initialization locks for static threads, not a thread waiting using any possible synchronization mechanism. There's no way for the static initialization mechanism to know that some other thread is waiting using some entirely different mechanism on another thread.
The quoted section is referring to the fact that the below example won't deadlock:
public class A
{
static A()
{
Thread.Sleep(TimeSpan.FromSeconds(1));
B.DoNothing();
}
public static void DoNothing() { }
}
public class B
{
static B()
{
Thread.Sleep(TimeSpan.FromSeconds(1));
A.DoNothing();
}
public static void DoNothing() { }
}
private static void Main()
{
Task.Run(() => B.DoNothing());
A.DoNothing();
}
This example doesn't deadlock because one thread is waiting for another thread to release the static initializer lock, so when that thread ends up asking for a static initializer lock that the original thread has, the quoted clause kicks in and it just skips the lock.