Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why no deadlock in this scenario?

Tags:

c#

.net

So I was happily reading this from Eric Lippert and then, of course, the excellent comments and in them John Payson said:

a more interesting example might have been to use two static classes, since such a program could deadlock without any visible blocking statements.

and I thought, yeah, that'd be easy so I knocked up this:

public static class A
{     
    static A()
    {
        Console.WriteLine("A.ctor");
        B.Initialize();
        Console.WriteLine("A.ctor.end");
    }

    public static void Initialize()
    {
        Console.WriteLine("A.Initialize");
    }
}
public static class B
{
    static B()
    {
        Console.WriteLine("B.ctor");
        A.Initialize();
        Console.WriteLine("B.ctor.end");
    }

    public static void Initialize()
    {
        Console.WriteLine("B.Initialize");
    }

    public static void Go()
    {
        Console.WriteLine("Go");
    }
}

The output of which (after calling B.Go()) is:

B.ctor
A.ctor
B.Initialize
A.ctor.end
A.Initialize
B.ctor.end
Go

No deadlock and I am am obviously a loser - so to perpetuate the embarrassment, here is my question: why no deadlock here?

It seems to my tiny brain that B.Initialize is called before the static constructor of B has finished and I thought that that was not allowed.

like image 855
kmp Avatar asked Feb 01 '13 07:02

kmp


1 Answers

It's not a deadlock because you're not doing anything that should block, nor are you doing anything that should break.

You are not using any resources from A within B, and vice versa. As a result, your circular dependency is "safe" in the sense that nothing will explode.

If you trace the path that your printouts show, then nothing should block:

  1. Call Go (I suspect)
  2. Enter B (static constructor) as it is not initialized.
  3. Print out
  4. Use A.Initialize()
  5. A's static constructor is required to execute first
  6. Print out
  7. Use B.Initialize()
  8. B does not need to initialize, but it's not in a completed state (fortunately no variables are being set, so nothing breaks)
  9. Print out, then return
  10. Print out (from A's static constructor), then return
  11. A.Initialize() can finally be called because A is initialized
  12. Print out, then return
  13. Print out (from B's static constructor), then return
  14. Go

The only thing that you really did was present the potential for an unsafe state: accessing a class whose constructor had not finished executing. That is unsafe code, and while not blocking, definitely represents a broken state.

like image 89
pickypg Avatar answered Oct 15 '22 01:10

pickypg