1 /**
2 * Copyright: Copyright Auburn Sounds 2015-2016.
3 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
4 * Authors: Guillaume Piolat
5 */
6 module dplug.core.runtime;
7
8 import dplug.core.fpcontrol;
9 import dplug.core.nogc;
10 import dplug.core.cpuid;
11
12 // Helpers to deal with the D runtime.
13 version = useShakyWorkaround;
14
15 /// When this version is defined, the runtime won't be initialized.
16 version = doNotUseRuntime;
17
18 version(OSX)
19 {
20 __gshared bool didInitRuntime = false;
21
22 version(useShakyWorkaround)
23 {
24 // Workaround Issue #15060
25 // https://issues.dlang.org/show_bug.cgi?id=15060
26 // Found by Martin Nowak and bitwise
27 // Trade-off a crash for a leak :|
28
29 extern(C) int sysctlbyname(const char *, void *, size_t *, void *, size_t);
30
31 alias dyld_image_states = int;
32 enum : dyld_image_states
33 {
34 dyld_image_state_initialized = 50,
35 }
36
37 extern(C) nothrow
38 {
39 alias dyld_image_state_change_handler = const(char)* function(dyld_image_states state, uint infoCount, void* dyld_image_info);
40 }
41
42 extern(C) const(char)* ignoreImageLoad(dyld_image_states state, uint infoCount, void* dyld_image_info) nothrow
43 {
44 return null;
45 }
46
47 extern(C) void dyld_register_image_state_change_handler(dyld_image_states state, bool batch, dyld_image_state_change_handler handler);
48 }
49
50 // Initializes the runtime if not already initialized
51 void runtimeInitWorkaround15060()
52 {
53 import core.runtime;
54
55 if(!didInitRuntime) // There is a race here, could it possibly be a problem? Until now, no.
56 {
57 Runtime.initialize();
58
59 version(useShakyWorkaround)
60 {
61 dyld_register_image_state_change_handler(dyld_image_state_initialized, false, &ignoreImageLoad);
62 }
63
64 didInitRuntime = true;
65 }
66 }
67 }
68
69 /// RAII struct to cover callbacks that need attachment and runtime initialized.
70 /// This deals with runtime inialization and thread attachment in a very explicit way.
71 struct ScopedForeignCallback(bool assumeRuntimeIsAlreadyInitialized,
72 bool saveRestoreFPU)
73 {
74 public:
75
76
77 // On Windows, we can assume that the runtime is initialized already by virtue of DLL_PROCESS_ATTACH
78 version(Windows)
79 enum bool doInitializeRuntime = false;
80 else
81 enum bool doInitializeRuntime = !assumeRuntimeIsAlreadyInitialized;
82
83 // Detaching threads when going out of callbacks.
84 // This avoid the GC pausing threads that are doing their things or have died since.
85 // This fixed #110 (Cubase + OS X), at a runtime cost.
86 enum bool detachThreadsAfterCallback = true;
87
88 /// Thread that shouldn't be attached are eg. the audio threads.
89 void enter()
90 {
91 debug _entered = true;
92
93 static if (saveRestoreFPU)
94 _fpControl.initialize();
95
96 version(doNotUseRuntime)
97 {
98 // Just detect the CPU
99 initializeCpuid();
100 }
101 else
102 {
103 // Runtime initialization if needed.
104 static if (doInitializeRuntime)
105 {
106 version(OSX)
107 runtimeInitWorkaround15060();
108 else
109 {
110 import core.runtime;
111 Runtime.initialize();
112 }
113
114 // CPUID detection
115 initializeCpuid();
116 }
117
118 import core.thread: thread_attachThis;
119
120 static if (detachThreadsAfterCallback)
121 {
122 bool alreadyAttached = isThisThreadAttached();
123 if (!alreadyAttached)
124 {
125 thread_attachThis();
126 _threadWasAttached = true;
127 }
128 }
129 else
130 {
131 thread_attachThis();
132 }
133 }
134 }
135
136 ~this()
137 {
138 version(doNotUseRuntime)
139 {
140 // Nothing to do, since thread was never attached
141 }
142 else
143 {
144 static if (detachThreadsAfterCallback)
145 if (_threadWasAttached)
146 {
147 import core.thread: thread_detachThis;
148 thread_detachThis();
149 }
150 }
151
152 // Ensure enter() was called.
153 debug assert(_entered);
154 }
155
156 @disable this(this);
157
158 private:
159
160 version(doNotUseRuntime)
161 {
162 }
163 else
164 {
165 static if (detachThreadsAfterCallback)
166 bool _threadWasAttached = false;
167 }
168
169 static if (saveRestoreFPU)
170 FPControl _fpControl;
171
172 debug bool _entered = false;
173
174 static bool isThisThreadAttached() nothrow
175 {
176 import core.memory;
177 import core.thread;
178 GC.disable(); scope(exit) GC.enable();
179 if (auto t = Thread.getThis())
180 return true;
181 else
182 return false;
183 }
184
185 }