1 /**
2     Threads
3 
4     Copyright:
5         Copyright © 2023-2025, Kitsunebi Games
6         Copyright © 2023-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.threading.thread;
12 import it = nulib.threading.internal.thread;
13 import numem;
14 
15 /**
16     Thread ID
17 */
18 alias ThreadId = it.ThreadId;
19 
20 /**
21     A thread of execution.
22 */
23 class Thread : NuObject {
24 private:
25     it.NativeThread thread_;
26 
27 protected:
28 
29     /**
30         The native implementation defined handle of the thread.
31     */
32     final @property it.NativeThread nativeHandle() => thread_;
33     
34     /**
35         Virtual function which may be overridden.
36     */
37     void onExecute() @nogc { }
38 
39     /**
40         Creates a new thread executing the "onExecute"
41         virtual function.
42     */
43     this() @nogc {
44         it.ThreadContext context_;
45         context_.userData = cast(void*)this;
46         context_.callback = (void* ctx) { (cast(Thread)ctx).onExecute(); };
47         this.thread_ = it.NativeThread.create(context_);
48     }
49 
50 public:
51 
52     /**
53         The Thread ID of the calling thread.
54     */
55     static @property ThreadId selfTid() @safe => it.NativeThread.selfTid();
56 
57     /**
58         The thread Id of the given thread.
59     */
60     final @property ThreadId tid() => thread_.tid();
61 
62     /**
63         Whether the thread is currently running.
64     */
65     final @property bool isRunning() => thread_.isRunning();
66 
67     /**
68         Creates a new thread.
69     */
70     this(void delegate() callback) @nogc @trusted {
71         it.ThreadContext context_;
72         context_.userData = callback.ptr;
73         context_.callback = cast(void function(void* ptr) @nogc)callback.funcptr;
74         this.thread_ = it.NativeThread.create(context_);
75     }
76 
77     /**
78         Creates a new thread.
79     */
80     this(void function() callback) @nogc @trusted {
81         it.ThreadContext context_;
82         context_.userData = cast(void*)this;
83         context_.callback = cast(void function(void* ptr) @nogc)callback;
84         this.thread_ = it.NativeThread.create(context_);
85     }
86 
87     /**
88         Creates a new thread.
89     */
90     this(T)(void function(T) callback, T data) @nogc @trusted {
91         it.ThreadContext context_;
92         context_.userData = cast(void*)data;
93         context_.callback = cast(void function(void* ptr) @nogc)callback;
94         this.thread_ = it.NativeThread.create(context_);
95     }
96 
97     /**
98         Makes the calling thread sleep for the specified amount
99         of time.
100 
101         Params:
102             ms = Miliseconds that the thread should sleep.
103     */
104     static void sleep(uint ms) @nogc @safe {
105         it.NativeThread.sleep(ms);
106     }
107 
108     /**
109         Starts executing the thread.
110 
111         Returns:
112             This thread instance, allowing chaining.
113     */
114     Thread start() @nogc @safe {
115         if (thread_)
116             thread_.start();
117         return this;
118     }
119 
120     /**
121         Forcefully cancels the thread, stopping execution.
122 
123         This is not a safe operation, as it may lead to memory
124         leaks and corrupt state. Only use when ABSOLUTELY neccesary.
125 
126         Returns:
127             This thread instance, allowing chaining.
128     */
129     Thread cancel() @nogc @system {
130         if (thread_)
131             thread_.cancel();
132         return this;
133     }
134 
135     /**
136         Blocks the calling thread until the given thread
137         completes.
138 
139         Params:
140             timeout =   How long to wait for the thread to exit, 
141                         0 to wait forever.
142             rethrow =   Whether exections should be rethrown into
143                         this thread.
144 
145         Returns:
146             Whether the thread exited within the given time.
147     */
148     bool join(uint timeout = 0, bool rethrow = false) @nogc @safe {
149         if (thread_)
150             return thread_.join(timeout, rethrow);
151         return false;
152     }
153 }
154 
155 @("start & join")
156 unittest {
157     import std.format : format;
158     __gshared uint v = 0;
159 
160     Thread t = nogc_new!Thread(() {
161         foreach(i; 0..100)
162             v += 1;
163     }).start();
164     t.join();
165 
166     assert(t.nativeHandle, "Thread handle was null!");
167     assert(v == 100, "Expected 100, got %s".format(v));
168     nogc_delete(t);
169 }
170 
171 @("isRunning")
172 unittest {
173     Thread t = nogc_new!Thread(() {
174         Thread.sleep(500);
175     }).start();
176     assert(t.isRunning);
177 
178     t.join();
179     nogc_delete(t);
180 }
181 
182 @("tid")
183 unittest {
184     Thread t = nogc_new!Thread(() {
185         Thread.sleep(500);
186     }).start();
187     assert(t.isRunning);
188     assert(t.tid() != Thread.selfTid());
189 
190     t.join();
191     nogc_delete(t);
192 }