module singleton;
import std.concurrency;
import core.atomic;
import core.thread;
class LockSingleton
{
static LockSingleton get()
{
__gshared LockSingleton _instance;
synchronized
{
if (_instance is null) {
_instance = new LockSingleton;
}
}
return _instance;
}
private:
this() { }
}
class SyncSingleton
{
static SyncSingleton get()
{
static bool _instantiated; // tls
__gshared SyncSingleton _instance;
if (!_instantiated)
{
synchronized
{
if (_instance is null) {
_instance = new SyncSingleton;
}
_instantiated = true;
}
}
return _instance;
}
private:
this() { }
}
class AtomicSingleton
{
static AtomicSingleton get()
{
shared static bool _instantiated = false;
__gshared AtomicSingleton _instance;
// only enter synchronized block if not instantiated
if (!atomicLoad!(MemoryOrder.acq)(_instantiated))
{
synchronized
{
if (_instance is null) {
_instance = new AtomicSingleton;
}
atomicStore!(MemoryOrder.rel)(_instantiated, true);
}
}
return _instance;
}
}
version (unittest)
{
ulong _thread_call_count; // TLS
}
unittest
{
import std.datetime;
import std.stdio;
import std.string;
import std.typetuple;
import std.exception;
import core.atomic;
foreach (TestClass; TypeTuple!(LockSingleton, SyncSingleton, AtomicSingleton))
{
// mixin to avoid multiple definition errors
mixin(q{
shared ulong msecs = 0;
static void test_%1$s(shared ulong* counter)
{
auto sw = StopWatch(AutoStart.yes);
foreach (i; 0 .. 1024_000)
{
// just trying to avoid the compiler from doing dead-code optimization
_thread_call_count += enforce(TestClass.get() !is null);
}
sw.stop();
atomicOp!"+="(*counter, sw.peek.msecs);
}
enum threadCount = 4;
foreach (i; 0 .. threadCount)
spawn(&test_%1$s, &msecs);
thread_joinAll();
}.format(TestClass.stringof));
writefln("Test time for %s: %s msecs.", TestClass.stringof, cast(double)msecs/threadCount);
}
}
void main() { }