views:

122

answers:

6

Here's an idea I just came up with for a safe, efficient way of handling Singleton synchronization issues. It's basically double-checked locking, but with a twist that involves thread-local storage. In Java/C#/D-style pseudocode, assuming __thread denotes thread-local storage for static variables:

class MySingleton {
    __thread static bool isInitialized; 
    static MySingleton instance; 

    static MySingleton getInstance() {
        if(!isInitialized) {
            synchronized {
                isInitialized = true;
                if(instance is null) {
                    instance = new MySingleton();
                }
            }
        }

        return instance;
   }
}

This is guaranteed to only enter the synchronized block once per thread throughout the entire life of the program. From that point forward, we get a simple check of a thread-local bool to see whether we've already entered the synchronized block and verified that the object is initialized from this thread.

+4  A: 

I do not think a language-agnostic (or platform-agnostic) approach is helpful here, because while the construct may be logically sound, there are implementation-specific gotchas that will prevent it from working properly. An example is the double-checked locking that just did not work on Java pre-5 because it was broken at the JVM level.

So you should be using the available language constructs or libraries on each platform.

For Java, you can get singletons using enum.

Thilo
the enum singleton in java is pretty slick.
dan
A: 

Looks good to me as far as Java is concerned. If you are going to do it this way, it is more conventional to make the thread-local storage a reference. No point in reading the static as well.

But in Java class loading is lazy and thread-safe, so you might as well just write:

private static final MySingleton instance = new MySingleton(); 

Or not use singletons at all.

Tom Hawtin - tackline
+2  A: 

The reason double-checked locking is broken (to the best of my knowledge) is the possibility that instance is not null but also not fully constructed due to read/write reordering.

Thread-local storage wouldn't solve anything. It might get you around from having to declare isInitialized as volatile, but that still doesn't fix your problem.

dan
`instance` is always assigned with the lock held. The first time any particular thread reads `instance` the lock is also held, so there is not a problem with partial construction.
Tom Hawtin - tackline
perhaps for java 6+, but not so much with .NET...though on x86 with the CLR you are okay. this blog explains it: http://www.bluebytesoftware.com/blog/PermaLink,guid,543d89ad-8d57-4a51-b7c9-a821e3992bf6.aspx
dan
that should be java 5+ in my previous comment.
dan
+2  A: 

This appears clean. The actual check and initialization of your instance object is inside a synchronized block and each thread is forced to enter the synchronized block on the first call, you get a clean happens-before edge between threads.

Since isInitialized is thread-local, why are you setting it inside the synchronized block? Also, you should only set isInitalized after constructing your singleton object. This way, if it hasn't been initialized yet and the constructor throws, this thread will check again the next time it's called.

    if(!isInitialized) {
        synchronized {
            if(instance is null) {
                instance = new MySingleton();
            }
        }
        isInitialized = true;
    }
R Samuel Klatchko
Accepting because you give good advice on how to improve this idiom.
dsimcha
A: 

Yes, under new jdk5 JMM if you declare instance volatile DCL will work

Loop
Also,the best approach when dealing with singletons is using IODH or enum (as specified in this thread)
Loop
@Loop What happens if you are initializing a volatile object and then referencing the field of that object that is not volatile in which is initialized in the constructor. You make reference to that objects field and you can still have a partially constructed object. Volatile does fix that one object you are creating but none of its nested objects.
John V.
John, that's the whole point with volatile: it prevents another thread to come along and read instance before it is finished initializing. So DCL problem with partial constructed objects is solved.Check the semantics of volatile arrays:http://jeremymanson.blogspot.com/2009/06/volatile-arrays-in-java.html
Loop
+1  A: 

Yes, this construct is safe under all the high level languages that I know of. In particular, it is safe in any language where the memory/concurrency models guarantee that a given thread always sees it's own operations in an order consistent with program order (which is pretty much any useful language), and where the synchronized block or equivalent provides the usual guarantees with respect to operations before, within and after the block.

BeeOnRope