This post originated from an RSS feed registered with .NET Buzz
by Steve Hebert.
Original Post: thread safety and static variable instantiation
Feed Title: Steve Hebert's Development Blog
Feed URL: /error.htm?aspxerrorpath=/blogs/steve.hebert/rss.aspx
Feed Description: .Steve's .Blog - Including .Net, SQL Server, .Math and everything in between
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:
\n
Â
\n
Allocate MyObject memory and assign it to tempObj
\n
Call Constructor on tempObj
\n
assign tempObj to _myObj
\n
Â
\n
Given the state of the ECMA spec, a valid implementation could be this:
\n
Â
\n
Allocate MyObject memory and assign it to _myObj
\n
Call Constructor on _myObj
\n
Â
\n
In this second implementation, consider what happens if the thread holding the lock gets preempted between Allocate and the Constructor call.\n A thread that doesn\'t hold the lock believes the construction of _myObj is complete and proceeds with a partially initialized variable. Doh!
\n
Â
\n
If I want to do double check locking in a truly safe manner that complies with the ECMA spec, I have to write:
\n
Â
\n
Â
\n
if( _myObj \u003d\u003d null )
\n
{
\n
",1]
);
//-->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)
\n
           {
\n
                       if( _myObj \u003d\u003d null )
\n
                       {
\n
                                   MyObject tempObj \u003d new MyObject(â¦);
\n
                                   _myObj \u003d tempObj;
\n
                       }
\n
           }
\n
}
\n
Â
\n
Interesting.
\n
Â
\n
As I mentioned earlier, the Microsoft version of the CLR enables the first version to run correctly. Because of the spec, it\'s a valid point of concern with non-Microsoft implementations of the CLR.\n  If you want to compare this behavior to Java, the Java memory model explicity doesn\'t support double-locking â even though it\'s commonly done.\n
\n
Â
\n
Â
\n",0]
);
//-->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.