1 /**
2  * This file provides `ScopedForeignCallback` to be used in every callback, and use to provide runtime initialization (now unused).
3  *
4  * Copyright: Copyright Auburn Sounds 2015-2016.
5  * License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
6  * Authors:   Guillaume Piolat
7  */
8 module dplug.core.runtime;
9 
10 import dplug.core.fpcontrol;
11 import dplug.core.nogc;
12 import dplug.core.cpuid;
13 
14 /// When this version is defined, the runtime won't be initialized.
15 version = doNotUseRuntime;
16 
17 /// RAII struct to cover callbacks that need attachment and runtime initialized.
18 /// This deals with runtime inialization and thread attachment in a very explicit way.
19 struct ScopedForeignCallback(bool assumeRuntimeIsAlreadyInitialized,
20                              bool saveRestoreFPU)
21 {
22 public:
23 
24 
25     // On Windows, we can assume that the runtime is initialized already by virtue of DLL_PROCESS_ATTACH
26     version(Windows)
27         enum bool doInitializeRuntime = false;
28     else
29         enum bool doInitializeRuntime = !assumeRuntimeIsAlreadyInitialized;
30 
31     // Detaching threads when going out of callbacks.
32     // This avoid the GC pausing threads that are doing their things or have died since.
33     // This fixed #110 (Cubase + OS X), at a runtime cost.
34     enum bool detachThreadsAfterCallback = true;
35 
36     /// Thread that shouldn't be attached are eg. the audio threads.
37     void enter()
38     {
39         debug _entered = true;
40 
41         static if (saveRestoreFPU)
42             _fpControl.initialize();
43 
44         version(doNotUseRuntime)
45         {
46             // Just detect the CPU
47             initializeCpuid();
48         }
49         else
50         {
51             // Runtime initialization if needed.
52             static if (doInitializeRuntime)
53             {
54                 import core.runtime;
55                 Runtime.initialize();
56 
57                 // CPUID detection
58                 initializeCpuid();
59             }
60 
61             import core.thread: thread_attachThis;
62 
63             static if (detachThreadsAfterCallback)
64             {
65                 bool alreadyAttached = isThisThreadAttached();
66                 if (!alreadyAttached)
67                 {
68                     thread_attachThis();
69                     _threadWasAttached = true;
70                 }
71             }
72             else
73             {
74                 thread_attachThis();
75             }
76         }
77     }
78 
79     ~this()
80     {
81         version(doNotUseRuntime)
82         {
83             // Nothing to do, since thread was never attached
84         }
85         else
86         {
87             static if (detachThreadsAfterCallback)
88                 if (_threadWasAttached)
89                 {
90                     import core.thread: thread_detachThis;
91                     thread_detachThis();
92                 }
93         }
94 
95         // Ensure enter() was called.
96         debug assert(_entered);
97     }
98 
99     @disable this(this);
100 
101 private:
102 
103     version(doNotUseRuntime)
104     {
105     }
106     else
107     {
108         static if (detachThreadsAfterCallback)
109             bool _threadWasAttached = false;
110     }
111 
112     static if (saveRestoreFPU)
113         FPControl _fpControl;
114 
115     debug bool _entered = false;
116 
117     static bool isThisThreadAttached() nothrow
118     {
119         import core.memory;
120         import core.thread;
121         GC.disable(); scope(exit) GC.enable();
122         if (auto t = Thread.getThis())
123             return true;
124         else
125             return false;
126     }
127 
128 }