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 }