1 module concurrency.utils; 2 3 import std.traits; 4 5 /// Helper used in with() to easily access `shared` methods of a class 6 struct SharedGuard(T) if (is(T == class)) { 7 import core.sync.mutex : Mutex; 8 private: 9 Mutex mutex; 10 public: 11 T reg; 12 alias reg this; 13 @disable this(); 14 @disable this(this); 15 static SharedGuard acquire(shared T reg, Mutex mutex) { 16 mutex.lock_nothrow(); 17 auto instance = SharedGuard.init; 18 instance.reg = (cast() reg); 19 instance.mutex = mutex; 20 return instance; 21 } 22 ~this() { 23 mutex.unlock_nothrow(); 24 } 25 } 26 27 /// A manually constructed closure, aimed at shared 28 struct Closure(Fun, Args...) { 29 Fun fun; 30 Args args; 31 auto apply() shared { 32 return fun((cast()this).args); 33 } 34 } 35 36 /// This is a terrible workaround for closure bugs in D. When a local is used in a delegate D is supposed to move it to the heap. I haven't seen that happen in all cases so we have this manual workaround. 37 auto closure(Fun, Args...)(Fun fun, Args args) @trusted if (isFunctionPointer!Fun) { 38 static assert(!hasFunctionAttributes!(Fun, "@system"), "no @system functions please"); 39 auto cl = cast(shared)new Closure!(Fun, Args)(fun, args); 40 /// need to cast to @safe because a @trusted delegate doesn't fit a @safe one... 41 static if (hasFunctionAttributes!(Fun, "nothrow")) 42 alias ResultType = void delegate() nothrow shared @safe; 43 else 44 alias ResultType = void delegate() shared @safe; 45 return cast(ResultType)&cl.apply; 46 } 47 48 /// don't want vibe-d to overwrite the scheduler 49 void resetScheduler() @trusted { 50 import std.concurrency : scheduler; 51 if (scheduler !is null) 52 scheduler = null; 53 } 54 55 enum NoVoid(T) = !is(T == void); 56 57 void spin_yield() nothrow @trusted @nogc { 58 // TODO: could use the pause asm instruction 59 // it is available in LDC as intrinsic... but not in DMD 60 import core.thread : Thread; 61 62 Thread.yield(); 63 } 64 65 /// ugly ugly 66 static if (__traits(compiles, () { import core.atomic : casWeak; }) && __traits(compiles, () { 67 import core.internal.atomic : atomicCompareExchangeWeakNoResult; 68 })) 69 public import core.atomic : casWeak; 70 else { 71 import core.atomic : MemoryOrder; 72 auto casWeak(MemoryOrder M1 = MemoryOrder.seq, MemoryOrder M2 = MemoryOrder.seq, T, V1, V2)(T* here, V1 ifThis, V2 writeThis) pure nothrow @nogc @safe { 73 import core.atomic : cas; 74 75 static if (__traits(compiles, cas!(M1, M2)(here, ifThis, writeThis))) 76 return cas!(M1, M2)(here, ifThis, writeThis); 77 else 78 return cas(here, ifThis, writeThis); 79 } 80 } 81 82 enum isThreadSafeFunction(alias Fun) = !hasFunctionAttributes!(Fun, "@system") && (isFunction!Fun || isFunctionPointer!Fun || hasFunctionAttributes!(Fun, "shared")); 83 84 enum isThreadSafeCallable(alias Fun) = (isAggregateType!Fun && isCallable!Fun && __traits(compiles, () @safe { shared Fun f; f(); })) || (isSomeFunction!Fun && isThreadSafeFunction!Fun); 85 86 // Loads a function from the main process. 87 // When using dynamic libraries globals and TLS variables are duplicated. 88 // We need a way to ensure globals used across dynamic libraries and the host 89 // are all pointing to the same instance. 90 // We do this by exporting accessors functions which a dynamic library can 91 // call to get access to the global. 92 auto dynamicLoad(alias fun)() nothrow @trusted { 93 alias Fn = typeof(&fun); 94 __gshared Fn fn; 95 96 if (fn is null) 97 fn = dynamicLoadRaw!fun; 98 99 // If dynamic loading fails we just pick the local function. 100 // This serves two purposes, 1) if users aren't using dynamic 101 // libraries and the application isn't compiled with the proper linker 102 // flags for exporting functions, it won't be found, and 2) it will 103 // reference the function so it won't be compiled away. 104 if(fn is null) 105 fn = &fun; 106 107 return fn; 108 } 109 110 auto dynamicLoadRaw(alias fun)() nothrow @trusted { 111 alias Fn = typeof(&fun); 112 version (Windows) { 113 import core.sys.windows.windows; 114 return cast(Fn) GetProcAddress(GetModuleHandle(null), fun.mangleof); 115 } else version (Posix) { 116 import core.sys.posix.dlfcn : dlopen, dlsym, dlerror, RTLD_LAZY; 117 auto parent = dlopen(null, RTLD_LAZY); 118 return cast(Fn) dlsym(parent, fun.mangleof); 119 } else static assert(false, "platform not supported"); 120 }