1 /** 2 POSIX 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.posix.threading.thread; 12 import nulib.threading.internal.thread; 13 import nulib.posix.threading.semaphore; 14 import nulib.posix.pthread; 15 import numem; 16 17 struct PosixThreadContext { 18 ThreadContext ctx; 19 PosixSemaphore semaphore; 20 } 21 22 class PosixThread : NativeThread { 23 private: 24 @nogc: 25 uint runRequests_; 26 shared(PosixThreadContext) ctx_; 27 pthread_t handle_; 28 29 public: 30 31 ~this() nothrow { 32 try { 33 PosixSemaphore sem = cast(PosixSemaphore)nu_atomic_load_ptr(cast(void**)&ctx_.semaphore); 34 if (sem) { 35 nogc_delete(sem); 36 nu_atomic_store_ptr(cast(void**)&ctx_.semaphore, null); 37 } 38 } catch (Exception ex) { 39 // Let it leak. 40 } 41 } 42 43 this(ThreadContext ctx) nothrow { 44 this.ctx_ = cast(shared(PosixThreadContext))PosixThreadContext( 45 ctx: ctx, 46 semaphore: nogc_new!PosixSemaphore(0), 47 ); 48 } 49 50 /** 51 ID of the thread. 52 */ 53 override 54 @property ThreadId tid() @trusted => cast(ThreadId)cast(void*)handle_; 55 56 /** 57 Whether the thread is currently running. 58 */ 59 override 60 @property bool isRunning() @trusted => nu_atomic_load_32(runRequests_) > 0; 61 62 /** 63 Starts the given thread. 64 */ 65 override 66 void start() @trusted { 67 if (nu_atomic_load_32(runRequests_) == 0) { 68 nu_atomic_add_32(runRequests_, 1); 69 70 pthread_attr_t pattr = void; 71 int err = pthread_attr_init(&pattr); 72 assert(err == 0, "Failed to create thread!"); 73 pthread_create(&handle_, &pattr, &_nu_thread_posix_entry, cast(void*)&ctx_); 74 pthread_attr_destroy(&pattr); 75 } 76 } 77 78 /** 79 Forcefully cancels the thread, stopping execution. 80 81 This is not a safe operation, as it may lead to memory 82 leaks and corrupt state. Only use when ABSOLUTELY neccesary. 83 */ 84 override 85 void cancel() @system { 86 if (nu_atomic_load_32(runRequests_) > 0) { 87 if (nu_atomic_sub_32(runRequests_, 1) == 1) { 88 pthread_cancel(handle_); 89 this.join(0, false); 90 } 91 } 92 } 93 94 /** 95 Waits for the thread to finish execution. 96 97 Params: 98 timeout = How long to wait for the thread to exit. 99 rethrow = Whether execptions thrown in the thread should be rethrown. 100 */ 101 override 102 bool join(uint timeout, bool rethrow) @trusted { 103 if (!isRunning) 104 return true; 105 106 void* retv; 107 if (timeout == 0) { 108 if (pthread_join(handle_, &retv) == 0) { 109 if (rethrow && ctx_.ctx.ex) 110 throw cast(Exception)ctx_.ctx.ex; 111 112 return true; 113 } 114 return false; 115 } 116 117 PosixSemaphore sem = cast(PosixSemaphore)nu_atomic_load_ptr(cast(void**)&ctx_.semaphore); 118 if (sem && sem.await(timeout)) { 119 if (pthread_join(handle_, &retv) == 0) { 120 if (rethrow && ctx_.ctx.ex) 121 throw cast(Exception)ctx_.ctx.ex; 122 123 return true; 124 } 125 return false; 126 } 127 return false; 128 } 129 } 130 131 extern(C) export 132 NativeThread _nu_thread_new(ThreadContext ctx) @trusted @nogc nothrow { 133 return nogc_new!PosixThread(ctx); 134 } 135 136 extern(C) export 137 ThreadId _nu_thread_current_tid() @trusted @nogc nothrow { 138 return cast(ThreadId)cast(void*)pthread_self(); 139 } 140 141 extern(C) export 142 void _nu_thread_sleep(uint ms) @trusted @nogc nothrow { 143 cast(void)usleep(cast(int)ms); 144 } 145 146 // 147 // BINDINGS 148 // 149 extern(C) @nogc nothrow: 150 151 alias fp2_t = extern(D) void function(void* userData) @nogc; 152 153 void* _nu_thread_posix_entry(void* threadContext) @trusted nothrow @nogc { 154 PosixThreadContext* context = cast(PosixThreadContext*)(threadContext); 155 156 // Signal semaphore if it's still alive. 157 PosixSemaphore sem = cast(PosixSemaphore)nu_atomic_load_ptr(cast(void**)&context.semaphore); 158 try { 159 (cast(fp2_t)context.ctx.callback)(context.ctx.userData); 160 if (sem) 161 sem.signal(); 162 } catch(Exception ex) { 163 context.ctx.ex = ex; 164 } 165 return null; 166 } 167 168 extern int usleep(int) @trusted @nogc nothrow;