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 }