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;
}
version (unittest) static void nullify() { _instance = null; }
private:
this() { }
__gshared LockSingleton _instance;
}
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;
}
version (unittest) static void nullify() { _instance = null; }
private:
this() { }
__gshared SyncSingleton _instance;
}
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) static void nullify() { _instance = null; _instantiated = false; }
private:
shared static bool _instantiated = false;
__gshared AtomicSingleton _instance;
}
version (unittest)
{
ulong _thread_call_count; // TLS
}
unittest
{
import std.algorithm;
import std.datetime;
import std.stdio;
import std.string;
import std.typetuple;
import std.exception;
import core.atomic;
enum numRuns = 10;
enum threadCount = 1024;
foreach(immutable _; 0 .. numRuns) {
foreach (TestClass; TypeTuple!(/*LockSingleton, */SyncSingleton, AtomicSingleton))
{
// mixin to avoid multiple definition errors
mixin(q{
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();
atomicStore(*counter, sw.peek.msecs);
}
// Not shared because dmd's Phobos won't allow to reduce() it later
ulong[threadCount] timers;
foreach (i; 0 .. threadCount)
spawn(&test_%1$s, cast(shared(ulong*))&timers[i]);
thread_joinAll();
auto grandTotal = timers.reduce!"a+b";
TestClass.nullify();
}.format(TestClass.stringof));
writefln("Test %s time for %s: %s msecs.", _, TestClass.stringof, cast(double)grandTotal/threadCount);
}
writeln;
}
}
void main() { }