A coworker asked me a question
today about initialization of static member variables and how they relate to
static constructors. He also told me about an article he read that the
following code isn't safe:
if( _myObj == null )
{
Lock( _lockObj )
{
If( _myObj == null )
_myObj = new MyObject(…);
}
}
He definitely had my attention ...
He passed along an article on
implementing a thread-safe singleton pattern in C# and the article is a very interesting
read. To be fair up front, the above code works in the microsoft
implementation (according to Chriss Brumme and discussed later), but the ECMA
spec is vague on how instantiation must take place. On the other hand,
the above code is explicitly NOT threadsafe in Java. The article also
discusses the way in which the .Net runtime chooses to instantiate static
member variables and this section was news to me.
Double Locking and Thread Safety
The above code looks right until you read Chris Brumme's Memory Model
blog
entry. The excerpt that finally makes sense after reading this a
couple of times is:
... (W)e have to assume that a series of stores have taken
place during construction of 'a'. Those stores can be arbitrarily
reordered, including the possibility of delaying them until after the
publishing store which assigns the new object to 'a'. At that point,
there is a small window before the store.release implied by leaving the
lock. Inside that window, other CPUs can navigate through the reference
'a' and see a partially constructed instance.
So what does this mean?
In the assignment of a new operator, this piece of code could be broken by a
weak but valid implementation of the ECMA spec. Given the
following line of code:
_myObj = new MyObject();
We expect that the underlying
pseudo code looks something like this:
Allocate MyObject memory and
assign address to tempObj
Call Constructor on tempObj
assign tempObj to _myObj
Given the state of the ECMA spec, a
valid implementation could be this:
Allocate MyObject memory and
assign it to _myObj
Call Constructor on _myObj
In this second implementation,
consider what happens if the thread holding the lock gets preempted during the
Constructor call. A thread that doesn't hold the lock believes the
construction of _myObj is complete and proceeds with a partially initialized variable.
If I want to do double check
locking in a truly safe manner that complies with the ECMA spec, I have to
write:
if( _myObj == null )
{
Lock(_lockObj)
{
if( _myObj == null )
{
MyObject tempObj = new MyObject(…);
_myObj = tempObj;
}
}
}
As I mentioned earlier, the
Microsoft version of the CLR enables the first version to run correctly (post
.Net 1.0). Because of the spec, it's a valid point of concern with
non-Microsoft implementations of the CLR that can be easily answered.
Given that double-lock checking in Java is not supported, it has me wondering...
is this a holdover from Java's days as a stack-based compiler? Can a
stack-based compiler create an object structure without first allocating the
memory to the host pointer and then calling the constructor?
Static Variable Instantiation
Another interesting part of the article discusses static member instantiation
and how the mechanics work. Given a class where two static members exist:
public class Ugly
{
public static Ug _Ug = new Ug();
public static Lee _Lee = new Lee();
}
The static variables are not created until I access one of the members which I
would expect. When I access one of the members before instantiating the
class, both static objects are actually instantiated before returning the
result. This was a surprise to me. It turns out the C# compiler marks
the class with beforefieldinit.
(The behaviors surrounding beforefieldinit
are interesting and certainly worth reading.) The way to get around
this behavior is to define the class as follows:
public UglyClass
{
public static Ug = new Ug();
public static Lee = new Lee();
static UglyClass() {}
}
When C# sees a static constructor, the type is not marked with the
beforefieldinit attribute. Therefore each static object is instantiated
upon first reference.
