1 /** 2 Win32 Implementation for nulib.threading.internal.thread 3 4 Copyright: 5 Copyright © 2025, Kitsunebi Games 6 Copyright © 2025, Inochi2D Project 7 8 License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 9 Authors: Luna Nielsen 10 */ 11 module nulib.win32.threading.thread; 12 import nulib.threading.internal.thread; 13 import nulib.win32.common; 14 import numem; 15 16 /** 17 Win32 implementation of native threading. 18 */ 19 class Win32Thread : NativeThread { 20 private: 21 @nogc: 22 uint suspendCount_; 23 shared(ThreadContext) ctx_; 24 HANDLE handle_; 25 uint tid_; 26 27 public: 28 29 // Destruction is not handled by the class destructor. 30 ~this() @trusted nothrow { } 31 32 /** 33 Creates a new thread from a context. 34 35 Params: 36 ctx = The thread context. 37 */ 38 this(ThreadContext ctx) @trusted nothrow { 39 this.ctx_ = cast(shared(ThreadContext))ctx; 40 nu_atomic_store_32(this.suspendCount_, 1); 41 handle_ = cast(HANDLE)_beginthreadex( 42 null, 43 0u, 44 &_nu_thread_win32_entry, 45 cast(void*)&ctx_, 46 CREATE_SUSPENDED, 47 &tid_ 48 ); 49 50 assert(handle_, "Failed to create thread!"); 51 } 52 53 /** 54 Creates a thread by providing an existing handle. 55 */ 56 this(HANDLE handle) @trusted { 57 this.handle_ = handle; 58 } 59 60 /** 61 Whether the thread is currently running. 62 */ 63 override @property bool isRunning() @trusted => nu_atomic_load_32(suspendCount_) == 0; 64 65 /** 66 ID of the thread. 67 */ 68 override @property ThreadId tid() @safe => cast(ThreadId)tid_; 69 70 /** 71 Starts the given thread. 72 */ 73 override 74 void start() @trusted { 75 if (!handle_) 76 return; 77 78 if (nu_atomic_load_32(suspendCount_) != 0) { 79 int sv = ResumeThread(handle_); 80 if (sv >= 0) 81 nu_atomic_store_32(suspendCount_, sv-1); 82 } 83 } 84 85 /** 86 Forcefully cancels the thread, stopping execution. 87 88 This is not a safe operation, as it may lead to memory 89 leaks and corrupt state. Only use when ABSOLUTELY neccesary. 90 */ 91 override 92 void cancel() @system { 93 if (!handle_) 94 return; 95 96 if (nu_atomic_load_32(suspendCount_) == 0) { 97 int sv = SuspendThread(handle_); 98 if (sv >= 0) 99 nu_atomic_store_32(suspendCount_, sv+1); 100 } 101 } 102 103 /** 104 Waits for the thread to finish execution. 105 106 Params: 107 timeout = How long to wait for the thread to exit. 108 rethrow = Whether execptions thrown in the thread should be rethrown. 109 */ 110 override 111 bool join(uint timeout, bool rethrow) @trusted { 112 113 // Thread is already suspended. 114 if (nu_atomic_load_32(suspendCount_) != 0) 115 return true; 116 117 // Thread is not suspended, wait for it. 118 if (WaitForSingleObject(handle_, timeout == 0 ? INFINITE : timeout) == WAIT_OBJECT_0) { 119 nu_atomic_store_32(suspendCount_, 1); 120 121 CloseHandle(handle_); 122 this.handle_ = null; 123 124 if (rethrow && ctx_.ex) 125 throw cast(Exception)ctx_.ex; 126 127 return true; 128 } 129 130 return false; 131 } 132 } 133 134 extern(C) export 135 NativeThread _nu_thread_new(ThreadContext ctx) @trusted @nogc nothrow { 136 return nogc_new!Win32Thread(ctx); 137 } 138 139 extern(C) export 140 ThreadId _nu_thread_current_tid() @trusted @nogc nothrow { 141 return cast(ThreadId)GetCurrentThreadId(); 142 } 143 144 extern(C) export 145 void _nu_thread_sleep(uint ms) @trusted @nogc nothrow { 146 Sleep(ms); 147 } 148 149 // 150 // BINDINGS 151 // 152 extern(Windows) @nogc nothrow: 153 enum CREATE_SUSPENDED = 0x00000004; 154 155 uint _nu_thread_win32_entry(void* threadContext) @trusted nothrow @nogc { 156 ThreadContext* context = cast(ThreadContext*)(threadContext); 157 try { 158 (cast(fp2_t)context.callback)(context.userData); 159 } catch(Exception ex) { 160 context.ex = ex; 161 } 162 return 0; 163 } 164 165 alias fp2_t = extern(D) void function(void* userData) @nogc; 166 alias fp_t = extern(Windows) uint function(void*); 167 168 extern void Sleep(int); 169 extern int ResumeThread(HANDLE); 170 extern int SuspendThread(HANDLE); 171 extern int GetCurrentThreadId(); 172 extern ptrdiff_t _beginthreadex(void*, uint, fp_t, void*, uint, uint*);