threading.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603
  1. #if defined(GB_SYSTEM_LINUX)
  2. #include <signal.h>
  3. #endif
  4. #if defined(GB_SYSTEM_WINDOWS)
  5. #pragma warning(push)
  6. #pragma warning(disable: 4505)
  7. #endif
  8. struct BlockingMutex;
  9. struct RecursiveMutex;
  10. struct Semaphore;
  11. struct Condition;
  12. struct Thread;
  13. struct ThreadPool;
  14. #define THREAD_PROC(name) isize name(struct Thread *thread)
  15. gb_internal THREAD_PROC(thread_pool_thread_proc);
  16. #define WORKER_TASK_PROC(name) isize name(void *data)
  17. typedef WORKER_TASK_PROC(WorkerTaskProc);
  18. typedef struct WorkerTask {
  19. WorkerTaskProc *do_work;
  20. void *data;
  21. } WorkerTask;
  22. struct Thread {
  23. #if defined(GB_SYSTEM_WINDOWS)
  24. void *win32_handle;
  25. #else
  26. pthread_t posix_handle;
  27. #endif
  28. isize idx;
  29. WorkerTask *queue;
  30. size_t capacity;
  31. std::atomic<uint64_t> head_and_tail;
  32. isize stack_size;
  33. struct ThreadPool *pool;
  34. };
  35. typedef std::atomic<int32_t> Futex;
  36. typedef volatile int32_t Footex;
  37. gb_internal void futex_wait(Futex *addr, Footex val);
  38. gb_internal void futex_signal(Futex *addr);
  39. gb_internal void mutex_init (BlockingMutex *m);
  40. gb_internal void mutex_destroy (BlockingMutex *m);
  41. gb_internal void mutex_lock (BlockingMutex *m);
  42. gb_internal bool mutex_try_lock(BlockingMutex *m);
  43. gb_internal void mutex_unlock (BlockingMutex *m);
  44. gb_internal void mutex_init (RecursiveMutex *m);
  45. gb_internal void mutex_destroy (RecursiveMutex *m);
  46. gb_internal void mutex_lock (RecursiveMutex *m);
  47. gb_internal bool mutex_try_lock(RecursiveMutex *m);
  48. gb_internal void mutex_unlock (RecursiveMutex *m);
  49. gb_internal void semaphore_init (Semaphore *s);
  50. gb_internal void semaphore_destroy(Semaphore *s);
  51. gb_internal void semaphore_post (Semaphore *s, i32 count);
  52. gb_internal void semaphore_wait (Semaphore *s);
  53. gb_internal void semaphore_release(Semaphore *s) { semaphore_post(s, 1); }
  54. gb_internal void condition_init(Condition *c);
  55. gb_internal void condition_destroy(Condition *c);
  56. gb_internal void condition_broadcast(Condition *c);
  57. gb_internal void condition_signal(Condition *c);
  58. gb_internal void condition_wait(Condition *c, BlockingMutex *m);
  59. gb_internal void condition_wait_with_timeout(Condition *c, BlockingMutex *m, u32 timeout_in_ms);
  60. gb_internal u32 thread_current_id(void);
  61. gb_internal void thread_init (ThreadPool *pool, Thread *t, isize idx);
  62. gb_internal void thread_init_and_start (ThreadPool *pool, Thread *t, isize idx);
  63. gb_internal void thread_join_and_destroy(Thread *t);
  64. gb_internal void thread_set_name (Thread *t, char const *name);
  65. gb_internal void yield_thread(void);
  66. gb_internal void yield_process(void);
  67. struct MutexGuard {
  68. MutexGuard() = delete;
  69. MutexGuard(MutexGuard const &) = delete;
  70. MutexGuard(BlockingMutex *bm) : bm{bm} {
  71. mutex_lock(this->bm);
  72. }
  73. MutexGuard(RecursiveMutex *rm) : rm{rm} {
  74. mutex_lock(this->rm);
  75. }
  76. MutexGuard(BlockingMutex &bm) : bm{&bm} {
  77. mutex_lock(this->bm);
  78. }
  79. MutexGuard(RecursiveMutex &rm) : rm{&rm} {
  80. mutex_lock(this->rm);
  81. }
  82. ~MutexGuard() {
  83. if (this->bm) {
  84. mutex_unlock(this->bm);
  85. } else if (this->rm) {
  86. mutex_unlock(this->rm);
  87. }
  88. }
  89. operator bool() const { return true; }
  90. BlockingMutex *bm;
  91. RecursiveMutex *rm;
  92. };
  93. #define MUTEX_GUARD_BLOCK(m) if (MutexGuard GB_DEFER_3(_mutex_guard_){m})
  94. #define MUTEX_GUARD(m) MutexGuard GB_DEFER_3(_mutex_guard_){m}
  95. #if defined(GB_SYSTEM_WINDOWS)
  96. struct BlockingMutex {
  97. SRWLOCK srwlock;
  98. };
  99. gb_internal void mutex_init(BlockingMutex *m) {
  100. }
  101. gb_internal void mutex_destroy(BlockingMutex *m) {
  102. }
  103. gb_internal void mutex_lock(BlockingMutex *m) {
  104. AcquireSRWLockExclusive(&m->srwlock);
  105. }
  106. gb_internal bool mutex_try_lock(BlockingMutex *m) {
  107. return !!TryAcquireSRWLockExclusive(&m->srwlock);
  108. }
  109. gb_internal void mutex_unlock(BlockingMutex *m) {
  110. ReleaseSRWLockExclusive(&m->srwlock);
  111. }
  112. struct RecursiveMutex {
  113. CRITICAL_SECTION win32_critical_section;
  114. };
  115. gb_internal void mutex_init(RecursiveMutex *m) {
  116. InitializeCriticalSection(&m->win32_critical_section);
  117. }
  118. gb_internal void mutex_destroy(RecursiveMutex *m) {
  119. DeleteCriticalSection(&m->win32_critical_section);
  120. }
  121. gb_internal void mutex_lock(RecursiveMutex *m) {
  122. EnterCriticalSection(&m->win32_critical_section);
  123. }
  124. gb_internal bool mutex_try_lock(RecursiveMutex *m) {
  125. return TryEnterCriticalSection(&m->win32_critical_section) != 0;
  126. }
  127. gb_internal void mutex_unlock(RecursiveMutex *m) {
  128. LeaveCriticalSection(&m->win32_critical_section);
  129. }
  130. struct Semaphore {
  131. void *win32_handle;
  132. };
  133. gb_internal void semaphore_init(Semaphore *s) {
  134. s->win32_handle = CreateSemaphoreA(NULL, 0, I32_MAX, NULL);
  135. }
  136. gb_internal void semaphore_destroy(Semaphore *s) {
  137. CloseHandle(s->win32_handle);
  138. }
  139. gb_internal void semaphore_post(Semaphore *s, i32 count) {
  140. ReleaseSemaphore(s->win32_handle, count, NULL);
  141. }
  142. gb_internal void semaphore_wait(Semaphore *s) {
  143. WaitForSingleObjectEx(s->win32_handle, INFINITE, FALSE);
  144. }
  145. struct Condition {
  146. CONDITION_VARIABLE cond;
  147. };
  148. gb_internal void condition_init(Condition *c) {
  149. }
  150. gb_internal void condition_destroy(Condition *c) {
  151. }
  152. gb_internal void condition_broadcast(Condition *c) {
  153. WakeAllConditionVariable(&c->cond);
  154. }
  155. gb_internal void condition_signal(Condition *c) {
  156. WakeConditionVariable(&c->cond);
  157. }
  158. gb_internal void condition_wait(Condition *c, BlockingMutex *m) {
  159. SleepConditionVariableSRW(&c->cond, &m->srwlock, INFINITE, 0);
  160. }
  161. gb_internal void condition_wait_with_timeout(Condition *c, BlockingMutex *m, u32 timeout_in_ms) {
  162. SleepConditionVariableSRW(&c->cond, &m->srwlock, timeout_in_ms, 0);
  163. }
  164. #else
  165. struct BlockingMutex {
  166. pthread_mutex_t pthread_mutex;
  167. };
  168. gb_internal void mutex_init(BlockingMutex *m) {
  169. pthread_mutex_init(&m->pthread_mutex, nullptr);
  170. }
  171. gb_internal void mutex_destroy(BlockingMutex *m) {
  172. pthread_mutex_destroy(&m->pthread_mutex);
  173. }
  174. gb_internal void mutex_lock(BlockingMutex *m) {
  175. pthread_mutex_lock(&m->pthread_mutex);
  176. }
  177. gb_internal bool mutex_try_lock(BlockingMutex *m) {
  178. return pthread_mutex_trylock(&m->pthread_mutex) == 0;
  179. }
  180. gb_internal void mutex_unlock(BlockingMutex *m) {
  181. pthread_mutex_unlock(&m->pthread_mutex);
  182. }
  183. struct RecursiveMutex {
  184. pthread_mutex_t pthread_mutex;
  185. pthread_mutexattr_t pthread_mutexattr;
  186. };
  187. gb_internal void mutex_init(RecursiveMutex *m) {
  188. pthread_mutexattr_init(&m->pthread_mutexattr);
  189. pthread_mutexattr_settype(&m->pthread_mutexattr, PTHREAD_MUTEX_RECURSIVE);
  190. pthread_mutex_init(&m->pthread_mutex, &m->pthread_mutexattr);
  191. }
  192. gb_internal void mutex_destroy(RecursiveMutex *m) {
  193. pthread_mutex_destroy(&m->pthread_mutex);
  194. }
  195. gb_internal void mutex_lock(RecursiveMutex *m) {
  196. pthread_mutex_lock(&m->pthread_mutex);
  197. }
  198. gb_internal bool mutex_try_lock(RecursiveMutex *m) {
  199. return pthread_mutex_trylock(&m->pthread_mutex) == 0;
  200. }
  201. gb_internal void mutex_unlock(RecursiveMutex *m) {
  202. pthread_mutex_unlock(&m->pthread_mutex);
  203. }
  204. #if defined(GB_SYSTEM_OSX)
  205. struct Semaphore {
  206. semaphore_t osx_handle;
  207. };
  208. gb_internal void semaphore_init (Semaphore *s) { semaphore_create(mach_task_self(), &s->osx_handle, SYNC_POLICY_FIFO, 0); }
  209. gb_internal void semaphore_destroy(Semaphore *s) { semaphore_destroy(mach_task_self(), s->osx_handle); }
  210. gb_internal void semaphore_post (Semaphore *s, i32 count) { while (count --> 0) semaphore_signal(s->osx_handle); }
  211. gb_internal void semaphore_wait (Semaphore *s) { semaphore_wait(s->osx_handle); }
  212. #elif defined(GB_SYSTEM_UNIX)
  213. struct Semaphore {
  214. sem_t unix_handle;
  215. };
  216. gb_internal void semaphore_init (Semaphore *s) { sem_init(&s->unix_handle, 0, 0); }
  217. gb_internal void semaphore_destroy(Semaphore *s) { sem_destroy(&s->unix_handle); }
  218. gb_internal void semaphore_post (Semaphore *s, i32 count) { while (count --> 0) sem_post(&s->unix_handle); }
  219. void semaphore_wait (Semaphore *s) { int i; do { i = sem_wait(&s->unix_handle); } while (i == -1 && errno == EINTR); }
  220. #else
  221. #error Implement Semaphore for this platform
  222. #endif
  223. struct Condition {
  224. pthread_cond_t pthread_cond;
  225. };
  226. gb_internal void condition_init(Condition *c) {
  227. pthread_cond_init(&c->pthread_cond, NULL);
  228. }
  229. gb_internal void condition_destroy(Condition *c) {
  230. pthread_cond_destroy(&c->pthread_cond);
  231. }
  232. gb_internal void condition_broadcast(Condition *c) {
  233. pthread_cond_broadcast(&c->pthread_cond);
  234. }
  235. gb_internal void condition_signal(Condition *c) {
  236. pthread_cond_signal(&c->pthread_cond);
  237. }
  238. gb_internal void condition_wait(Condition *c, BlockingMutex *m) {
  239. pthread_cond_wait(&c->pthread_cond, &m->pthread_mutex);
  240. }
  241. gb_internal void condition_wait_with_timeout(Condition *c, BlockingMutex *m, u32 timeout_in_ms) {
  242. struct timespec abstime = {};
  243. abstime.tv_sec = timeout_in_ms/1000;
  244. abstime.tv_nsec = cast(long)(timeout_in_ms%1000)*1e6;
  245. pthread_cond_timedwait(&c->pthread_cond, &m->pthread_mutex, &abstime);
  246. }
  247. #endif
  248. gb_internal u32 thread_current_id(void) {
  249. u32 thread_id;
  250. #if defined(GB_SYSTEM_WINDOWS)
  251. #if defined(GB_ARCH_32_BIT) && defined(GB_CPU_X86)
  252. thread_id = (cast(u32 *)__readfsdword(24))[9];
  253. #elif defined(GB_ARCH_64_BIT) && defined(GB_CPU_X86)
  254. thread_id = (cast(u32 *)__readgsqword(48))[18];
  255. #else
  256. thread_id = GetCurrentThreadId();
  257. #endif
  258. #elif defined(GB_SYSTEM_OSX) && defined(GB_ARCH_64_BIT)
  259. thread_id = pthread_mach_thread_np(pthread_self());
  260. #elif defined(GB_ARCH_32_BIT) && defined(GB_CPU_X86)
  261. __asm__("mov %%gs:0x08,%0" : "=r"(thread_id));
  262. #elif defined(GB_ARCH_64_BIT) && defined(GB_CPU_X86)
  263. __asm__("mov %%fs:0x10,%0" : "=r"(thread_id));
  264. #elif defined(GB_SYSTEM_LINUX)
  265. thread_id = gettid();
  266. #else
  267. #error Unsupported architecture for thread_current_id()
  268. #endif
  269. return thread_id;
  270. }
  271. gb_internal gb_inline void yield_thread(void) {
  272. #if defined(GB_SYSTEM_WINDOWS)
  273. _mm_pause();
  274. #elif defined(GB_SYSTEM_OSX)
  275. #if defined(GB_CPU_X86)
  276. __asm__ volatile ("" : : : "memory");
  277. #elif defined(GB_CPU_ARM)
  278. __asm__ volatile ("yield" : : : "memory");
  279. #endif
  280. #elif defined(GB_CPU_X86)
  281. _mm_pause();
  282. #elif defined(GB_CPU_ARM)
  283. __asm__ volatile ("yield" : : : "memory");
  284. #else
  285. #error Unknown architecture
  286. #endif
  287. }
  288. gb_internal gb_inline void yield(void) {
  289. #if defined(GB_SYSTEM_WINDOWS)
  290. YieldProcessor();
  291. #else
  292. sched_yield();
  293. #endif
  294. }
  295. #if defined(GB_SYSTEM_WINDOWS)
  296. gb_internal DWORD __stdcall internal_thread_proc(void *arg) {
  297. Thread *t = cast(Thread *)arg;
  298. thread_pool_thread_proc(t);
  299. return 0;
  300. }
  301. #else
  302. gb_internal void *internal_thread_proc(void *arg) {
  303. #if (GB_SYSTEM_LINUX)
  304. // NOTE: Don't permit any signal delivery to threads on Linux.
  305. sigset_t mask = {};
  306. sigfillset(&mask);
  307. GB_ASSERT_MSG(pthread_sigmask(SIG_BLOCK, &mask, nullptr) == 0, "failed to block signals");
  308. #endif
  309. Thread *t = cast(Thread *)arg;
  310. thread_pool_thread_proc(t);
  311. return NULL;
  312. }
  313. #endif
  314. gb_internal void thread_init(ThreadPool *pool, Thread *t, isize idx) {
  315. gb_zero_item(t);
  316. #if defined(GB_SYSTEM_WINDOWS)
  317. t->win32_handle = INVALID_HANDLE_VALUE;
  318. #else
  319. t->posix_handle = 0;
  320. #endif
  321. t->capacity = 1 << 14; // must be a power of 2
  322. t->queue = (WorkerTask *)calloc(sizeof(WorkerTask), t->capacity);
  323. t->head_and_tail = 0;
  324. t->pool = pool;
  325. t->idx = idx;
  326. }
  327. gb_internal void thread_init_and_start(ThreadPool *pool, Thread *t, isize idx) {
  328. thread_init(pool, t, idx);
  329. isize stack_size = 0;
  330. #if defined(GB_SYSTEM_WINDOWS)
  331. t->win32_handle = CreateThread(NULL, stack_size, internal_thread_proc, t, 0, NULL);
  332. GB_ASSERT_MSG(t->win32_handle != NULL, "CreateThread: GetLastError");
  333. #else
  334. {
  335. pthread_attr_t attr;
  336. pthread_attr_init(&attr);
  337. defer (pthread_attr_destroy(&attr));
  338. pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
  339. if (stack_size != 0) {
  340. pthread_attr_setstacksize(&attr, stack_size);
  341. }
  342. pthread_create(&t->posix_handle, &attr, internal_thread_proc, t);
  343. }
  344. #endif
  345. }
  346. gb_internal void thread_join_and_destroy(Thread *t) {
  347. #if defined(GB_SYSTEM_WINDOWS)
  348. WaitForSingleObject(t->win32_handle, INFINITE);
  349. CloseHandle(t->win32_handle);
  350. t->win32_handle = INVALID_HANDLE_VALUE;
  351. #else
  352. pthread_join(t->posix_handle, NULL);
  353. t->posix_handle = 0;
  354. #endif
  355. }
  356. gb_internal void thread_set_name(Thread *t, char const *name) {
  357. #if defined(GB_COMPILER_MSVC)
  358. #pragma pack(push, 8)
  359. typedef struct {
  360. DWORD type;
  361. char const *name;
  362. DWORD id;
  363. DWORD flags;
  364. } gbprivThreadName;
  365. #pragma pack(pop)
  366. gbprivThreadName tn;
  367. tn.type = 0x1000;
  368. tn.name = name;
  369. tn.id = GetThreadId(cast(HANDLE)t->win32_handle);
  370. tn.flags = 0;
  371. __try {
  372. RaiseException(0x406d1388, 0, gb_size_of(tn)/4, cast(ULONG_PTR *)&tn);
  373. } __except(1 /*EXCEPTION_EXECUTE_HANDLER*/) {
  374. }
  375. #elif defined(GB_SYSTEM_WINDOWS) && !defined(GB_COMPILER_MSVC)
  376. // IMPORTANT TODO(bill): Set thread name for GCC/Clang on windows
  377. return;
  378. #elif defined(GB_SYSTEM_OSX)
  379. // TODO(bill): Test if this works
  380. pthread_setname_np(name);
  381. #elif defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD)
  382. pthread_set_name_np(t->posix_handle, name);
  383. #else
  384. // TODO(bill): Test if this works
  385. pthread_setname_np(t->posix_handle, name);
  386. #endif
  387. }
  388. #if defined(GB_SYSTEM_LINUX)
  389. #include <linux/futex.h>
  390. #include <sys/syscall.h>
  391. gb_internal void futex_signal(Futex *addr) {
  392. for (;;) {
  393. int ret = syscall(SYS_futex, addr, FUTEX_WAKE | FUTEX_PRIVATE_FLAG, 1, NULL, NULL, 0);
  394. if (ret == -1) {
  395. perror("Futex wake");
  396. GB_PANIC("Failed in futex wake!\n");
  397. } else if (ret > 0) {
  398. return;
  399. }
  400. }
  401. }
  402. gb_internal void futex_wait(Futex *addr, Footex val) {
  403. for (;;) {
  404. int ret = syscall(SYS_futex, addr, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, val, NULL, NULL, 0);
  405. if (ret == -1) {
  406. if (errno != EAGAIN) {
  407. perror("Futex wait");
  408. GB_PANIC("Failed in futex wait!\n");
  409. } else {
  410. return;
  411. }
  412. } else if (ret == 0) {
  413. if (*addr != val) {
  414. return;
  415. }
  416. }
  417. }
  418. }
  419. #elif defined(GB_SYSTEM_FREEBSD)
  420. #include <sys/types.h>
  421. #include <sys/umtx.h>
  422. gb_internal void futex_signal(Futex *addr) {
  423. _umtx_op(addr, UMTX_OP_WAKE, 1, 0, 0);
  424. }
  425. gb_internal void futex_wait(Futex *addr, Footex val) {
  426. for (;;) {
  427. int ret = _umtx_op(addr, UMTX_OP_WAIT_UINT, val, 0, NULL);
  428. if (ret == 0) {
  429. if (errno == ETIMEDOUT || errno == EINTR) {
  430. continue;
  431. }
  432. perror("Futex wait");
  433. GB_PANIC("Failed in futex wait!\n");
  434. } else if (ret == 0) {
  435. if (*addr != val) {
  436. return;
  437. }
  438. }
  439. }
  440. }
  441. #elif defined(GB_SYSTEM_OPENBSD)
  442. #include <sys/futex.h>
  443. gb_internal void futex_signal(Futex *addr) {
  444. for (;;) {
  445. int ret = futex((volatile uint32_t *)addr, FUTEX_WAKE | FUTEX_PRIVATE_FLAG, 1, NULL, NULL);
  446. if (ret == -1) {
  447. if (errno == ETIMEDOUT || errno == EINTR) {
  448. continue;
  449. }
  450. perror("Futex wake");
  451. GB_PANIC("futex wake fail");
  452. } else if (ret == 1) {
  453. return;
  454. }
  455. }
  456. }
  457. gb_internal void futex_wait(Futex *addr, Footex val) {
  458. for (;;) {
  459. int ret = futex((volatile uint32_t *)addr, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, val, NULL, NULL);
  460. if (ret == -1) {
  461. if (*addr != val) {
  462. return;
  463. }
  464. if (errno == ETIMEDOUT || errno == EINTR) {
  465. continue;
  466. }
  467. perror("Futex wait");
  468. GB_PANIC("Failed in futex wait!\n");
  469. }
  470. }
  471. }
  472. #elif defined(GB_SYSTEM_OSX)
  473. #define UL_COMPARE_AND_WAIT 0x00000001
  474. #define ULF_NO_ERRNO 0x01000000
  475. extern "C" int __ulock_wait(uint32_t operation, void *addr, uint64_t value, uint32_t timeout); /* timeout is specified in microseconds */
  476. extern "C" int __ulock_wake(uint32_t operation, void *addr, uint64_t wake_value);
  477. gb_internal void futex_signal(Futex *addr) {
  478. for (;;) {
  479. int ret = __ulock_wake(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, addr, 0);
  480. if (ret >= 0) {
  481. return;
  482. }
  483. if (ret == EINTR || ret == EFAULT) {
  484. continue;
  485. }
  486. if (ret == ENOENT) {
  487. return;
  488. }
  489. GB_PANIC("Failed in futex wake!\n");
  490. }
  491. }
  492. gb_internal void futex_wait(Futex *addr, Footex val) {
  493. for (;;) {
  494. int ret = __ulock_wait(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, addr, val, 0);
  495. if (ret >= 0) {
  496. if (*addr != val) {
  497. return;
  498. }
  499. continue;
  500. }
  501. if (ret == EINTR || ret == EFAULT) {
  502. continue;
  503. }
  504. if (ret == ENOENT) {
  505. return;
  506. }
  507. GB_PANIC("Failed in futex wait!\n");
  508. }
  509. }
  510. #elif defined(GB_SYSTEM_WINDOWS)
  511. gb_internal void futex_signal(Futex *addr) {
  512. WakeByAddressSingle((void *)addr);
  513. }
  514. gb_internal void futex_wait(Futex *addr, Footex val) {
  515. for (;;) {
  516. WaitOnAddress(addr, (void *)&val, sizeof(val), INFINITE);
  517. if (*addr != val) break;
  518. }
  519. }
  520. #endif
  521. #if defined(GB_SYSTEM_WINDOWS)
  522. #pragma warning(pop)
  523. #endif