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 }