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*);