1 /** 2 POSIX Implementation for nulib.threading.internal.semaphore 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.semaphore; 12 import nulib.threading.internal.semaphore; 13 import nulib.posix.common; 14 import numem; 15 16 version (OSX) 17 version = Darwin; 18 else version (iOS) 19 version = Darwin; 20 else version (TVOS) 21 version = Darwin; 22 else version (WatchOS) 23 version = Darwin; 24 else version (VisionOS) 25 version = Darwin; 26 27 /** 28 Native implementation of a semaphore. 29 */ 30 class PosixSemaphore : NativeSemaphore { 31 private: 32 @nogc: 33 version(Darwin) semaphore_t sem_; 34 else version(Posix) sem_t sem_; 35 36 public: 37 38 /// Destructor 39 ~this() nothrow { 40 version(Darwin) { 41 auto rc = semaphore_destroy(mach_task_self(), sem_); 42 assert(rc == KERN_SUCCESS, "Failed to destroy semaphore!"); 43 } else { 44 auto rc = sem_destroy(&sem_); 45 assert(rc == 0, "Failed to destroy semaphore!"); 46 } 47 } 48 49 /** 50 Constructs a new Posix Semaphore. 51 */ 52 this(uint count) nothrow { 53 version(Darwin) { 54 auto rc = semaphore_create(mach_task_self(), &sem_, SYNC_POLICY_FIFO, count); 55 assert(rc == KERN_SUCCESS, "Failed to create semaphore!"); 56 } else { 57 auto rc = sem_init(&sem_, 0, count); 58 assert(rc == 0, "Failed to create semaphore!"); 59 } 60 } 61 62 /** 63 Signals the semaphore. 64 65 Note: 66 Control is not transferred to the waiter. 67 */ 68 override 69 void signal() { 70 version(Darwin) { 71 auto rc = semaphore_signal(sem_); 72 assert(rc == KERN_SUCCESS, "Failed to signal semaphore!"); 73 } else { 74 auto rc = sem_post(&sem_); 75 assert(rc == 0, "Failed to signal semaphore!"); 76 } 77 } 78 79 /** 80 Suspends the thread until the semaphore is signaled, 81 or the timeout is reached. 82 83 Params: 84 timeout = Timeout in miliseconds to block the 85 calling thread before giving up. 86 87 Returns: 88 $(D true) if the semaphore was signaled in time, 89 $(D false) otherwise. 90 */ 91 override 92 bool await(ulong timeout = 0) { 93 version(Darwin) { 94 if (timeout == 0) { 95 while(true) { 96 auto rc = semaphore_wait(sem_); 97 if (!rc) 98 return true; 99 100 if (rc == KERN_ABORTED && errno == EINTR) 101 continue; 102 103 assert(0, "Unable to wait for semaphore!"); 104 return false; 105 } 106 } 107 108 mach_timespec_t t; 109 t.tv_sec = cast(uint)(timeout / 1000); 110 t.tv_nsec = cast(uint)(timeout % 1000); 111 while(true) { 112 auto rc = semaphore_timedwait(sem_, t); 113 if (!rc) 114 return true; 115 116 if (rc == KERN_OPERATION_TIMED_OUT) 117 return false; 118 119 if (rc == KERN_ABORTED && errno == EINTR) 120 continue; 121 122 assert(0, "Unable to wait for semaphore!"); 123 return false; 124 } 125 } else { 126 if (timeout == 0) { 127 auto rc = sem_wait(&sem_); 128 if (!rc) 129 return true; 130 131 assert(errno == EINTR, "Unable to wait for semaphore!"); 132 return false; 133 } 134 135 timespec t; 136 cast(void)clock_gettime(0, &t); 137 t.secs += (timeout / 1000); 138 t.nsecs = (t.nsecs + timeout) % 1000; 139 while(true) { 140 if (!sem_timedwait(&sem_, &t)) 141 return true; 142 143 if (errno == ETIMEDOUT) 144 return false; 145 146 if (errno == EINTR) 147 continue; 148 149 assert(0, "Unable to wait for semaphore!"); 150 return false; 151 } 152 } 153 } 154 155 /** 156 Checks if the semaphore is signalled then 157 awaits on it if is. 158 159 Returns: 160 $(D true) if the semaphore was signalled, 161 $(D false) otherwise. 162 */ 163 override 164 bool tryAwait() { 165 version(Darwin) { 166 mach_timespec_t t; 167 t.tv_sec = 0; 168 t.tv_nsec = 0; 169 while(true) { 170 auto rc = semaphore_timedwait(sem_, t); 171 if (!rc) 172 return true; 173 174 if (rc == KERN_OPERATION_TIMED_OUT) 175 return false; 176 177 if (rc == KERN_ABORTED && __error() == EINTR) 178 continue; 179 180 assert(0, "Unable to wait for semaphore!"); 181 return false; 182 } 183 } else { 184 while(true) { 185 if (!sem_trywait(&sem_)) 186 return true; 187 188 if (errno == EAGAIN) 189 return false; 190 191 if (errno == EINTR) 192 continue; 193 194 assert(0, "Unable to wait for semaphore!"); 195 return false; 196 } 197 } 198 } 199 } 200 201 202 extern(C) export 203 NativeSemaphore _nu_semaphore_new(uint count) @trusted @nogc nothrow { 204 return nogc_new!PosixSemaphore(count); 205 } 206 207 // 208 // BINDINGS 209 // 210 extern(C) @nogc nothrow: 211 212 version(Darwin) { 213 alias semaphore_t = uint; 214 alias task_t = uint; 215 216 alias kern_return_t = int; 217 enum kern_return_t KERN_SUCCESS = 0; 218 enum SYNC_POLICY_FIFO = 0x0; 219 enum KERN_ABORTED = 14; // Aborted (Kernel) 220 enum KERN_OPERATION_TIMED_OUT = 49; // Timed Out (Kernel) 221 222 alias clock_res_t = int; 223 struct mach_timespec_t { 224 uint tv_sec; 225 clock_res_t tv_nsec; 226 } 227 228 extern task_t mach_task_self(); 229 extern kern_return_t semaphore_create(task_t, semaphore_t*, int, int); 230 extern kern_return_t semaphore_destroy(task_t, semaphore_t); 231 extern kern_return_t semaphore_signal(semaphore_t); 232 extern kern_return_t semaphore_wait(semaphore_t); 233 extern kern_return_t semaphore_timedwait(semaphore_t, mach_timespec_t); 234 235 } else version(Posix) { 236 237 struct sem_t { 238 void[32] internal_; 239 } 240 241 struct timespec { 242 long secs; 243 long nsecs; 244 } 245 246 enum SEM_FAILED = cast(sem_t*) null; 247 248 extern int sem_destroy(sem_t*); 249 extern int sem_init(sem_t*, int, uint); 250 extern int sem_post(sem_t*); 251 extern int sem_trywait(sem_t*); 252 extern int sem_timedwait(sem_t*, const scope timespec*); 253 extern int sem_wait(sem_t*); 254 extern int clock_gettime(int, const scope timespec*); 255 }