Simple and fast Singleton pattern for ya.

W

William Stacey

FYI.
/// <summary>
/// Author: William Stacey
/// Fast and simple way to implement a singleton pattern without resorting
/// to nested classes or other static vodo. Can also be easily converted to
/// allow "n" number of instances to be created.
/// </summary>
public class SingletonClass
{
// Vars used by singleton logic.
private static int created = 0; // No object created yet.
private static SingletonClass singletonInstance = null;
// End singleton vars.

// Other private/public vars as needed in class.
private string name;

public SingletonClass()
{
name = "MyName";
Console.WriteLine("Created MyName.");
//other stuff...
}

/// <summary>
/// Returns single instance of SingletonClass if it exists. Otherwise
/// create it, store the reference and return the singleton.
/// This uses very fast Interlocked method that does its work via CPU
/// instructions in an atomic way. Almost as fast as an "if" test, while
/// being thread safe and not requiring kernel mode switch or other
locking.
/// </summary>
/// <returns>The singleton object.</returns>
public static SingletonClass GetInstance()
{
if ( Interlocked.Exchange(ref created, 1) == 1 )
return singletonInstance;
singletonInstance = new SingletonClass();
return singletonInstance;
}

/// <summary>
/// Reset "created" to zero. Next call to GetInstance() will create
/// a new SingletonClass obj. Can be used as to "refresh" and create
/// a new object daily or per some other event. This method is thread
safe.
/// </summary>
public static void ResetSingleton()
{
Interlocked.Exchange(ref created, 0);
}

public string Name
{
get { return this.name; }
}
}
 
J

Jon Skeet [C# MVP]

William Stacey said:
FYI.
/// <summary>
/// Author: William Stacey
/// Fast and simple way to implement a singleton pattern without resorting
/// to nested classes or other static vodo. Can also be easily converted to
/// allow "n" number of instances to be created.
/// </summary>

That's not threadsafe. There are two main problems:

1) Threads could end up with a return value of null if they call
GetInstance just after another call to GetInstance which has set the
"created" flag but not actually created the instance yet.

2) Threads could end up with a reference to the singleton before it has
finished being constructed - the reference assignment could be
rearranged to before the content of the actual constructor. You don't
have any memory barriers, so there's no guaranteed ordering of the
reads and writes.
 
W

William Stacey

Thanks for seeing that Jon. There was a small window there. Here is the
fix to correct for that possibility.
We use a lock once, in the case where multiple threads actually try to get
the first instance at the "same" time.
The first thread to get the lock, will create the instance. If multiple
threads happen to wait on the lock at same time before the instance is
created, they will "wake" up to see instance was created and return it. Any
future threads will not hit the lock, but just do the quick
interlocked.compareexchange to test for created. So except for the noted
exception on first use, no expensive locks are hit. Cheers!

/// <summary>
/// Fast and simple way to implement a singleton pattern without resorting
/// to nested classes or other static vodo. Can also be easily converted to
/// allow "n" number of instances to be created.
/// </summary>
public class SingletonClass
{
// Vars used by singleton logic.
private static int created = 0; // No object created yet.
private static SingletonClass singletonInstance = null;
//Our private lock object. We could have used typeof(SingletonClass)
instead.
private static object syncLock = new object();
// End singleton vars.

// Other private/public vars as needed in class.
private string name;

public SingletonClass()
{
name = "MyName";
Console.WriteLine("Created MyName.");
}

/// <summary>
/// Returns single instance of SingletonClass if it exists. Otherwise
/// create it, store the reference and return the singleton.
/// This uses very fast Interlocked method that does its work via CPU
/// instructions in an atomic way. Almost as fast as an "if" test, while
/// being thread safe and not requiring kernel mode switch or other
locking.
/// </summary>
/// <returns>The singleton object.</returns>
public static SingletonClass GetInstance()
{
// If instance already created, "created" var will be 1, so return
instance.
if ( Interlocked.CompareExchange(ref created, 1, 1) == 1 )
return singletonInstance;

// Singleton not created yet, get the lock to create
// the instance atomically. We only incur lock penalty on first
// use/creation. After that, we don't hit this lock and rely only
// on the test above to see if instance created.
lock(syncLock)
{
// If two, or more, threads happen to be wait on lock at same time
// (i.e. threads herding at startup. ) check again as the first thread to
get the lock
// would have already created the singleton. In the unlikely event that
the thread happened
// to die or be aborted between the three lines before it sets "created"
to 1, this still works
// as long as "lock" gives the lock to another waiting thread - which is
does.
if ( created == 1 )
return singletonInstance;

//Singleton not create yet, so create it and set the created flag
//using InterlockedExchange. Flag not set until Singleton created and
reference var set.
singletonInstance = new SingletonClass();
Interlocked.Exchange(ref created, 1);
}
return singletonInstance;
}

public string Name
{
get { return this.name; }
}
}
 
J

Jon Skeet [C# MVP]

William Stacey said:
Thanks for seeing that Jon. There was a small window there. Here is the
fix to correct for that possibility.
We use a lock once, in the case where multiple threads actually try to get
the first instance at the "same" time.
The first thread to get the lock, will create the instance. If multiple
threads happen to wait on the lock at same time before the instance is
created, they will "wake" up to see instance was created and return it. Any
future threads will not hit the lock, but just do the quick
interlocked.compareexchange to test for created. So except for the noted
exception on first use, no expensive locks are hit. Cheers!

Nope, you've then got the same problem as the normal double-check lock
algorithm - you could see the reference to the singleton before it's
properly initialised.

You basically need a memory barrier there *somewhere*.
 
W

William Stacey [MVP]

Before getting too far down a path here. Let me ask a related question.
Lets take one of the more common static implemtation as example:
public class Singleton
{
static readonly Singleton instance=new Singleton();
static Singleton()
{
}
Singleton()
{
}
public static Singleton GetInstance()
{
return instance;
}
}

Where is the read barrier here? Ok its executed once and created on first
call to GetInstance() and the instance ref is set to the ref of the object.
So far, so good. However, thread2 on processor2 does not know this and
calls GetInstance() right after thread1. Where is thread2's read barrier
for the instance and instance vars in the object?
Is there an explicit (unseen) read barrier performed on all static vars?
 
J

Jon Skeet [C# MVP]

William Stacey said:
Before getting too far down a path here. Let me ask a related question.
Lets take one of the more common static implemtation as example:
public class Singleton
{
static readonly Singleton instance=new Singleton();
static Singleton()
{
}
Singleton()
{
}
public static Singleton GetInstance()
{
return instance;
}
}

Where is the read barrier here?

I believe it's in the check to see that the type is initialized in the
first place, which must happen in its entirety (with a corresponding
write barrier) before the thread gets any further.
Ok its executed once and created on first
call to GetInstance() and the instance ref is set to the ref of the object.
So far, so good. However, thread2 on processor2 does not know this and
calls GetInstance() right after thread1. Where is thread2's read barrier
for the instance and instance vars in the object?
Is there an explicit (unseen) read barrier performed on all static vars?

All static members I believe, yes - at least until the JIT knows that
the type is definitely initialized. (That in itself would presumably
require a single read barrier per thread, but thereafter there's no
need for *another* read barrier.)

That's my understanding of it, but to be honest it *is* a difficult
area.
 
W

William Stacey [MVP]

All static members I believe, yes - at least until the JIT knows that
the type is definitely initialized. (That in itself would presumably
require a single read barrier per thread, but thereafter there's no
need for *another* read barrier.)

If that is true, then my example would benefit from the same read barrier as
using a static var. No? Agreed this is a gray area (at least for me).
 
J

Jon Skeet [C# MVP]

William Stacey said:
If that is true, then my example would benefit from the same read barrier as
using a static var. No? Agreed this is a gray area (at least for me).

No, because the read barrier could occur before the write barrier which
happens when you exit the monitor. You need the write barrier to occur
before the read barrier, which it does with normal synchronized data
access and with the "simple" singleton.
 
W

William Stacey [MVP]

No, because the read barrier could occur before the write barrier which
happens when you exit the monitor. You need the write barrier to occur
before the read barrier, which it does with normal synchronized data
access and with the "simple" singleton.

The thread2 can't see ref until thread1 creates the object *and stores the
ref. Then it should benefit from the same read barrier that the other
static design does on read (the one we don't see). The following example
shows a more explicit implementation in respect to the ref var itself. The
ref itself is definitely "barriered" on both reads and writes. My question
now would be the read barrier for the internal fields of the class.
However, I have the same question regarding the static implementation.

public static SingletonClass GetInstance()
{
// If instance already created, ref will not be null, so return instance.
if ( Interlocked.CompareExchange(ref singletonInstance, null, null) !=
null )
return (SingletonClass)singletonInstance;
else
{
lock(syncLock)
{
if ( singletonInstance != null )
return (SingletonClass)singletonInstance;
SingletonClass tmpSC = new SingletonClass();
Interlocked.Exchange(ref singletonInstance, tmpSC);
return (SingletonClass)singletonInstance;
}
}
}
 
J

Jon Skeet [C# MVP]

William Stacey said:
The thread2 can't see ref until thread1 creates the object *and stores the
ref.

Not sure what you mean here. Two threads can both enter the GetInstance
method before anything else happens. At that stage, both threads know
that the type has been initialized, so there needn't be any more read
barriers. The "dodgy" thread can then see assignments in any order so
long as all of the ones before the lock is exited are seen before all
the ones after the lock is exited - I think!
Then it should benefit from the same read barrier that the other
static design does on read (the one we don't see). The following example
shows a more explicit implementation in respect to the ref var itself. The
ref itself is definitely "barriered" on both reads and writes. My question
now would be the read barrier for the internal fields of the class.
However, I have the same question regarding the static implementation.

I think the key thing is that the memory barrier is only incurred until
the thread knows that the type has definitely been initialized.

Unfortunately I don't know anyone on the group with a deeper
understanding of this issue - I'm sure I've got some things wrong, and
I'd like to know what.
 
W

William Stacey [MVP]

I think the key thing is that the memory barrier is only incurred until
the thread knows that the type has definitely been initialized.

Not beat this into the ground, but it is interesting. Can we assume that
line 2 does not get hit until line 1 is fully created and returns ref to
tmpSC or no?

1) SingletonClass tmpSC = new SingletonClass();
2) return;
 
J

Jon Skeet [C# MVP]

William Stacey said:
Not beat this into the ground, but it is interesting. Can we assume that
line 2 does not get hit until line 1 is fully created and returns ref to
tmpSC or no?

1) SingletonClass tmpSC = new SingletonClass();
2) return;

Not sure, to be honest.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top