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 }