1 /**
2 This file provides `ScopedForeignCallback` to be used in every callback, and use to provide runtime initialization (now unused).
3 
4 Copyright: Guillaume Piolat 2015-2016.
5 License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
6 */
7 module dplug.core.runtime;
8 
9 import core.runtime;
10 import core.atomic;
11 import core.stdc.stdlib;
12 
13 import std.traits;
14 import std.functional: toDelegate;
15 
16 import dplug.core.fpcontrol;
17 import dplug.core.nogc;
18 
19 
20 /// RAII struct to cover extern callbacks.
21 /// This only deals with CPU identification and FPU control words save/restore.
22 struct ScopedForeignCallback(bool dummyDeprecated, bool saveRestoreFPU)
23 {
24 public:
25 nothrow:
26 @nogc:
27 
28     /// Thread that shouldn't be attached are eg. the audio threads.
29     void enter()
30     {
31         debug _entered = true;
32 
33         static if (saveRestoreFPU)
34             _fpControl.initialize();
35     }
36 
37     ~this()
38     {
39         // Ensure enter() was called.
40         debug assert(_entered);
41     }
42 
43     @disable this(this);
44 
45 private:
46 
47     static if (saveRestoreFPU)
48         FPControl _fpControl;
49 
50     debug bool _entered = false;
51 }
52 
53 /// "RUNTIME SECTION"
54 /// A function or method that can _use_ the runtime thanks to thread attachment.
55 ///
56 /// Runtime initialization must be dealt with externally with a `ScopedRuntime` struct
57 /// (typically long-lived since there can be only one).
58 ///
59 /// Warning: every GC object is reclaimed at the end of the runtime section.
60 ///          By using GC.addRoot you can make GC object survive the collection.
61 ///
62 /// Returns: a callback Voldement inside which you can use the runtime, but you can't escape GC memory.
63 auto runtimeSection(F)(F functionOrDelegateThatCanBeGC) nothrow @nogc if (isCallable!(F))
64 {
65     // turn that into a delegate for simplicity purposes
66     auto myGCDelegate = toDelegate(functionOrDelegateThatCanBeGC);
67     alias T = typeof(myGCDelegate);
68 
69     static ReturnType!T internalFunc(T fun, Parameters!T params) nothrow
70     {
71         import core.stdc.stdio;
72         ScopedRuntimeSection section;
73         section.enter();
74 
75         // Important: we only support `nothrow` runtime section.
76         // Supporting exception was creating spurious bugs, probably the excpeption being collected.
77         return fun(params);
78     }
79 
80     // We return this callable Voldemort type
81 
82     static struct ManualDelegate
83     {
84         typeof(myGCDelegate.ptr) ptr;
85         typeof(myGCDelegate.funcptr) funcptr;
86 
87         ReturnType!T opCall(Parameters!T params) nothrow @nogc
88         {
89             T dg;
90             dg.funcptr = funcptr;
91             dg.ptr = ptr;
92             return assumeNoGC(&internalFunc)(dg, params);
93         }
94 
95         @disable this(this);
96     }
97 
98     ManualDelegate fakeDg;
99     fakeDg.funcptr = myGCDelegate.funcptr;
100     fakeDg.ptr = myGCDelegate.ptr;
101     return fakeDg;
102 }
103 
104 /// RAII struct for runtime initialization, to be used once by the plug-in client.
105 /// Without underlying `ScopedRuntime`, there can be no `runtimeSection`.
106 struct ScopedRuntime
107 {
108 public:
109 nothrow:
110 @nogc:
111 
112     void initialize()
113     {
114         try
115         {
116             bool initOK = assumeNoGC(&Runtime.initialize)();
117 
118             if (!initOK)
119                 assert(false, "Runtime initialization shouldn't fail");
120         }
121         catch(Exception e)
122         {
123             assert(false, "Runtime initialization shouldn't fail");
124         }
125 
126         version(OSX)
127         {
128             atomicStore(_initialized, true);
129         }
130         else
131         {
132             _initialized = true;
133         }
134     }
135 
136     ~this()
137     {
138         version(OSX){}
139         else
140         {
141             if (_initialized)
142             {
143                 bool terminated;
144                 try
145                 {
146                     terminated = assumeNoGC(&Runtime.terminate)();
147                 }
148                 catch(Exception e)
149                 {
150                     terminated = false;
151                 }
152                 assert(terminated);
153                 _initialized = false;
154             }
155         }
156     }
157 
158     @disable this(this);
159 
160 private:
161 
162     version(OSX)
163     {
164         // Note: this is shared across plug-in instantiation
165         // TODO: use zero-init mutex once it exist
166         static shared(bool) _initialized = false;
167     }
168     else
169     {
170         bool _initialized = false;
171     }
172 }
173 
174 private:
175 
176 version(OSX)
177 {
178     version(DigitalMars)
179     {
180         // Workaround for DMDFE Issue #18456, can't use  pragma(crt_destructor) with -lib
181         // See_also https://issues.dlang.org/show_bug.cgi?id=18456
182         // So we leak the runtime.
183         // this means GC objects' destructors won't be called (not that you should rely on this anyway.
184     }
185     else
186     {
187         // We need that new feature because on OSX shared libraries share their runtime and globals
188         // This is called at termination of the host program
189         extern(C) pragma(crt_destructor) void deactivateDRuntime()
190         {
191             import core.stdc.stdio;
192 
193             bool initialized = atomicLoad(ScopedRuntime._initialized);
194 
195             if (initialized)
196             {
197                 try
198                 {
199                     bool terminated = assumeNoGC(&Runtime.terminate)();
200                 }
201                 catch(Exception e)
202                 {
203                 }
204                 atomicStore(ScopedRuntime._initialized, false); // TODO: this atomic is racey
205             }
206         }
207     }
208 }
209 
210 
211 
212 /// RAII struct to ensure thread attacment is initialized and usable
213 /// => that allow to use GC, TLS etc in a single function.
214 /// This isn't meant to be used directly, and it should certainly only be used in a scoped
215 /// manner without letting a registered thread exit.
216 struct ScopedRuntimeSection
217 {
218     import core.thread: thread_attachThis, thread_detachThis;
219 
220 public:
221 nothrow:
222     /// Thread that shouldn't be attached are eg. the audio threads.
223     void enter()
224     {
225         // shoud allow reentrant threads
226         bool alreadyAttached = isThisThreadAttached();
227         if (!alreadyAttached)
228         {
229             try
230             {
231                 thread_attachThis();
232             }
233             catch(Exception e)
234             {
235                 assert(false, "thread_attachThis is not supposed to fail");
236             }
237             _threadWasAttached = true;
238         }
239     }
240 
241     ~this()
242     {
243         // Detach current thread if it was attached by this runtime section
244         if (_threadWasAttached)
245         {
246             try
247             {
248                 thread_detachThis();
249             }
250             catch(Exception e)
251             {
252                 assert(false, "thread_detachThis is not supposed to fail");
253             }
254             _threadWasAttached = false;
255         }
256 
257         // By collecting here we avoid correctness by coincidence for someone
258         // that would rely on things remaining valid out of the ScopedRuntimeSection
259         debug
260         {
261             import core.memory;
262             GC.collect();
263         }
264     }
265 
266     @disable this(this);
267 
268 private:
269 
270     bool _threadWasAttached = false;
271 
272     static bool isThisThreadAttached() nothrow
273     {
274         import core.memory;
275         import core.thread;
276         GC.disable(); scope(exit) GC.enable();
277         if (auto t = Thread.getThis())
278             return true;
279         else
280             return false;
281     }
282 }
283 
284 
285