1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108 |
- #if defined(GB_SYSTEM_LINUX)
- #include <signal.h>
- #if __has_include(<valgrind/helgrind.h>)
- #include <valgrind/helgrind.h>
- #define HAS_VALGRIND
- #endif
- #endif
- #if defined(GB_SYSTEM_WINDOWS)
- #pragma warning(push)
- #pragma warning(disable: 4505)
- #endif
- #if defined(HAS_VALGRIND)
- #define ANNOTATE_LOCK_PRE(m, t) VALGRIND_HG_MUTEX_LOCK_PRE(m, t)
- #define ANNOTATE_LOCK_POST(m) VALGRIND_HG_MUTEX_LOCK_POST(m)
- #define ANNOTATE_UNLOCK_PRE(m) VALGRIND_HG_MUTEX_UNLOCK_PRE(m)
- #define ANNOTATE_UNLOCK_POST(m) VALGRIND_HG_MUTEX_UNLOCK_POST(m)
- #define ANNOTATE_SEM_WAIT_POST(s) VALGRIND_HG_SEM_WAIT_POST(s)
- #define ANNOTATE_SEM_POST_PRE(s) VALGRIND_HG_SEM_POST_PRE(s)
- #else
- #define ANNOTATE_LOCK_PRE(m, t)
- #define ANNOTATE_LOCK_POST(m)
- #define ANNOTATE_UNLOCK_PRE(m)
- #define ANNOTATE_UNLOCK_POST(m)
- #define ANNOTATE_SEM_WAIT_POST(s)
- #define ANNOTATE_SEM_POST_PRE(s)
- #endif
- struct BlockingMutex;
- struct RecursiveMutex;
- struct RwMutex;
- struct Semaphore;
- struct Condition;
- struct Thread;
- struct ThreadPool;
- struct Parker;
- #define THREAD_PROC(name) isize name(struct Thread *thread)
- gb_internal THREAD_PROC(thread_pool_thread_proc);
- #define WORKER_TASK_PROC(name) isize name(void *data)
- typedef WORKER_TASK_PROC(WorkerTaskProc);
- typedef struct WorkerTask {
- WorkerTaskProc *do_work;
- void *data;
- } WorkerTask;
- typedef struct TaskRingBuffer {
- std::atomic<isize> size;
- std::atomic<WorkerTask *> buffer;
- } TaskRingBuffer;
- typedef struct TaskQueue {
- std::atomic<isize> top;
- std::atomic<isize> bottom;
- std::atomic<TaskRingBuffer *> ring;
- } TaskQueue;
- struct Thread {
- #if defined(GB_SYSTEM_WINDOWS)
- void *win32_handle;
- #else
- pthread_t posix_handle;
- #endif
- isize idx;
- isize stack_size;
- struct TaskQueue queue;
- struct ThreadPool *pool;
- };
- typedef std::atomic<i32> Futex;
- typedef volatile i32 Footex;
- gb_internal void futex_wait(Futex *addr, Footex val);
- gb_internal void futex_signal(Futex *addr);
- gb_internal void futex_broadcast(Futex *addr);
- gb_internal void mutex_lock (BlockingMutex *m);
- gb_internal bool mutex_try_lock(BlockingMutex *m);
- gb_internal void mutex_unlock (BlockingMutex *m);
- gb_internal void mutex_lock (RecursiveMutex *m);
- gb_internal bool mutex_try_lock(RecursiveMutex *m);
- gb_internal void mutex_unlock (RecursiveMutex *m);
- gb_internal void rw_mutex_lock (RwMutex *m);
- gb_internal bool rw_mutex_try_lock (RwMutex *m);
- gb_internal void rw_mutex_unlock (RwMutex *m);
- gb_internal void rw_mutex_shared_lock (RwMutex *m);
- gb_internal bool rw_mutex_try_shared_lock(RwMutex *m);
- gb_internal void rw_mutex_shared_unlock (RwMutex *m);
- gb_internal void semaphore_post(Semaphore *s, i32 count);
- gb_internal void semaphore_wait(Semaphore *s);
- gb_internal void condition_broadcast(Condition *c);
- gb_internal void condition_signal(Condition *c);
- gb_internal void condition_wait(Condition *c, BlockingMutex *m);
- gb_internal void park(Parker *p);
- gb_internal void unpark_one(Parker *p);
- gb_internal void unpark_all(Parker *p);
- gb_internal u32 thread_current_id(void);
- gb_internal void thread_init (ThreadPool *pool, Thread *t, isize idx);
- gb_internal void thread_init_and_start (ThreadPool *pool, Thread *t, isize idx);
- gb_internal void thread_join_and_destroy(Thread *t);
- gb_internal void thread_set_name (Thread *t, char const *name);
- gb_internal void yield_thread(void);
- gb_internal void yield_process(void);
- struct Wait_Signal {
- Futex futex;
- };
- gb_internal void wait_signal_until_available(Wait_Signal *ws) {
- if (ws->futex.load() == 0) {
- futex_wait(&ws->futex, 0);
- }
- }
- gb_internal void wait_signal_set(Wait_Signal *ws) {
- ws->futex.store(1);
- futex_broadcast(&ws->futex);
- }
- struct MutexGuard {
- MutexGuard() = delete;
- MutexGuard(MutexGuard const &) = delete;
- MutexGuard(MutexGuard &&) = delete;
- explicit MutexGuard(BlockingMutex *bm) noexcept : bm{bm} {
- mutex_lock(this->bm);
- }
- explicit MutexGuard(RecursiveMutex *rm) noexcept : rm{rm} {
- mutex_lock(this->rm);
- }
- explicit MutexGuard(RwMutex *rwm) noexcept : rwm{rwm} {
- rw_mutex_lock(this->rwm);
- }
- explicit MutexGuard(BlockingMutex &bm) noexcept : bm{&bm} {
- mutex_lock(this->bm);
- }
- explicit MutexGuard(RecursiveMutex &rm) noexcept : rm{&rm} {
- mutex_lock(this->rm);
- }
- explicit MutexGuard(RwMutex &rwm) noexcept : rwm{&rwm} {
- rw_mutex_lock(this->rwm);
- }
- ~MutexGuard() noexcept {
- if (this->bm) {
- mutex_unlock(this->bm);
- } else if (this->rm) {
- mutex_unlock(this->rm);
- } else if (this->rwm) {
- rw_mutex_unlock(this->rwm);
- }
- }
- operator bool() const noexcept { return true; }
- BlockingMutex *bm;
- RecursiveMutex *rm;
- RwMutex *rwm;
- };
- #define MUTEX_GUARD_BLOCK(m) if (MutexGuard GB_DEFER_3(_mutex_guard_){m})
- #define MUTEX_GUARD(m) mutex_lock(m); defer (mutex_unlock(m))
- #define RW_MUTEX_GUARD(m) rw_mutex_lock(m); defer (rw_mutex_unlock(m))
- struct RecursiveMutex {
- Futex owner;
- i32 recursion;
- };
- gb_internal void mutex_lock(RecursiveMutex *m) {
- Futex tid;
- tid.store(cast(i32)thread_current_id());
- for (;;) {
- i32 prev_owner = 0;
- m->owner.compare_exchange_strong(prev_owner, tid, std::memory_order_acquire, std::memory_order_acquire);
- if (prev_owner == 0 || prev_owner == tid) {
- m->recursion++;
- // inside the lock
- return;
- }
- futex_wait(&m->owner, prev_owner);
- }
- }
- gb_internal bool mutex_try_lock(RecursiveMutex *m) {
- Futex tid;
- tid.store(cast(i32)thread_current_id());
- i32 prev_owner = 0;
- m->owner.compare_exchange_strong(prev_owner, tid, std::memory_order_acquire, std::memory_order_acquire);
- if (prev_owner == 0 || prev_owner == tid) {
- m->recursion++;
- // inside the lock
- return true;
- }
- return false;
- }
- gb_internal void mutex_unlock(RecursiveMutex *m) {
- m->recursion--;
- if (m->recursion != 0) {
- return;
- }
- m->owner.exchange(0, std::memory_order_release);
- futex_signal(&m->owner);
- // outside the lock
- }
- struct Semaphore {
- Footex count_;
- Futex &count() noexcept {
- return *(Futex *)&this->count_;
- }
- Futex const &count() const noexcept {
- return *(Futex *)&this->count_;
- }
- };
- gb_internal void semaphore_post(Semaphore *s, i32 count) {
- s->count().fetch_add(count, std::memory_order_release);
- if (s->count().load() == 1) {
- futex_signal(&s->count());
- } else {
- futex_broadcast(&s->count());
- }
- }
- gb_internal void semaphore_wait(Semaphore *s) {
- for (;;) {
- i32 original_count = s->count().load(std::memory_order_relaxed);
- while (original_count == 0) {
- futex_wait(&s->count(), original_count);
- original_count = s->count().load(std::memory_order_relaxed);
- }
- if (s->count().compare_exchange_strong(original_count, original_count-1, std::memory_order_acquire, std::memory_order_acquire)) {
- return;
- }
- }
- }
- #if defined(GB_SYSTEM_WINDOWS)
- struct BlockingMutex {
- SRWLOCK srwlock;
- };
- gb_internal void mutex_lock(BlockingMutex *m) {
- AcquireSRWLockExclusive(&m->srwlock);
- }
- gb_internal bool mutex_try_lock(BlockingMutex *m) {
- return !!TryAcquireSRWLockExclusive(&m->srwlock);
- }
- gb_internal void mutex_unlock(BlockingMutex *m) {
- ReleaseSRWLockExclusive(&m->srwlock);
- }
- struct Condition {
- CONDITION_VARIABLE cond;
- };
- gb_internal void condition_broadcast(Condition *c) {
- WakeAllConditionVariable(&c->cond);
- }
- gb_internal void condition_signal(Condition *c) {
- WakeConditionVariable(&c->cond);
- }
- gb_internal void condition_wait(Condition *c, BlockingMutex *m) {
- SleepConditionVariableSRW(&c->cond, &m->srwlock, INFINITE, 0);
- }
- struct RwMutex {
- SRWLOCK srwlock;
- };
- gb_internal void rw_mutex_lock(RwMutex *m) {
- AcquireSRWLockExclusive(&m->srwlock);
- }
- gb_internal bool rw_mutex_try_lock(RwMutex *m) {
- return !!TryAcquireSRWLockExclusive(&m->srwlock);
- }
- gb_internal void rw_mutex_unlock(RwMutex *m) {
- ReleaseSRWLockExclusive(&m->srwlock);
- }
- gb_internal void rw_mutex_shared_lock(RwMutex *m) {
- AcquireSRWLockShared(&m->srwlock);
- }
- gb_internal bool rw_mutex_try_shared_lock(RwMutex *m) {
- return !!TryAcquireSRWLockShared(&m->srwlock);
- }
- gb_internal void rw_mutex_shared_unlock(RwMutex *m) {
- ReleaseSRWLockShared(&m->srwlock);
- }
- #else
- enum Internal_Mutex_State : i32 {
- Internal_Mutex_State_Unlocked = 0,
- Internal_Mutex_State_Locked = 1,
- Internal_Mutex_State_Waiting = 2,
- };
- struct BlockingMutex {
- #if defined(HAS_VALGRIND)
- // BlockingMutex() {
- // VALGRIND_HG_MUTEX_INIT_POST(this, 0);
- // }
- // ~BlockingMutex() {
- // VALGRIND_HG_MUTEX_DESTROY_PRE(this);
- // }
- #endif
- i32 state_;
- Futex &state() {
- return *(Futex *)&this->state_;
- }
- Futex const &state() const {
- return *(Futex const *)&this->state_;
- }
- };
- gb_no_inline gb_internal void mutex_lock_slow(BlockingMutex *m, i32 curr_state) {
- i32 new_state = curr_state;
- for (i32 spin = 0; spin < 100; spin++) {
- i32 state = Internal_Mutex_State_Unlocked;
- bool ok = m->state().compare_exchange_weak(state, new_state, std::memory_order_acquire, std::memory_order_consume);
- if (ok) {
- return;
- }
- if (state == Internal_Mutex_State_Waiting) {
- break;
- }
- for (i32 i = gb_min(spin+1, 32); i > 0; i--) {
- yield_thread();
- }
- }
- // Set just in case 100 iterations did not do it
- new_state = Internal_Mutex_State_Waiting;
- for (;;) {
- if (m->state().exchange(Internal_Mutex_State_Waiting, std::memory_order_acquire) == Internal_Mutex_State_Unlocked) {
- return;
- }
- futex_wait(&m->state(), new_state);
- yield_thread();
- }
- }
- gb_internal void mutex_lock(BlockingMutex *m) {
- ANNOTATE_LOCK_PRE(m, 0);
- i32 v = m->state().exchange(Internal_Mutex_State_Locked, std::memory_order_acquire);
- if (v != Internal_Mutex_State_Unlocked) {
- mutex_lock_slow(m, v);
- }
- ANNOTATE_LOCK_POST(m);
- }
- gb_internal bool mutex_try_lock(BlockingMutex *m) {
- ANNOTATE_LOCK_PRE(m, 1);
- i32 v = m->state().exchange(Internal_Mutex_State_Locked, std::memory_order_acquire);
- if (v == Internal_Mutex_State_Unlocked) {
- ANNOTATE_LOCK_POST(m);
- return true;
- }
- return false;
- }
- gb_no_inline gb_internal void mutex_unlock_slow(BlockingMutex *m) {
- futex_signal(&m->state());
- }
- gb_internal void mutex_unlock(BlockingMutex *m) {
- ANNOTATE_UNLOCK_PRE(m);
- i32 v = m->state().exchange(Internal_Mutex_State_Unlocked, std::memory_order_release);
- switch (v) {
- case Internal_Mutex_State_Unlocked:
- GB_PANIC("Unreachable");
- break;
- case Internal_Mutex_State_Locked:
- // Okay
- break;
- case Internal_Mutex_State_Waiting:
- mutex_unlock_slow(m);
- break;
- }
- ANNOTATE_UNLOCK_POST(m);
- }
- struct Condition {
- i32 state_;
- Futex &state() {
- return *(Futex *)&this->state_;
- }
- Futex const &state() const {
- return *(Futex const *)&this->state_;
- }
- };
- gb_internal void condition_broadcast(Condition *c) {
- c->state().fetch_add(1, std::memory_order_release);
- futex_broadcast(&c->state());
- }
- gb_internal void condition_signal(Condition *c) {
- c->state().fetch_add(1, std::memory_order_release);
- futex_signal(&c->state());
- }
- gb_internal void condition_wait(Condition *c, BlockingMutex *m) {
- i32 state = c->state().load(std::memory_order_relaxed);
- mutex_unlock(m);
- futex_wait(&c->state(), state);
- mutex_lock(m);
- }
- struct RwMutex {
- // TODO(bill): make this a proper RW mutex
- BlockingMutex mutex;
- };
- gb_internal void rw_mutex_lock(RwMutex *m) {
- mutex_lock(&m->mutex);
- }
- gb_internal bool rw_mutex_try_lock(RwMutex *m) {
- return mutex_try_lock(&m->mutex);
- }
- gb_internal void rw_mutex_unlock(RwMutex *m) {
- mutex_unlock(&m->mutex);
- }
- gb_internal void rw_mutex_shared_lock(RwMutex *m) {
- mutex_lock(&m->mutex);
- }
- gb_internal bool rw_mutex_try_shared_lock(RwMutex *m) {
- return mutex_try_lock(&m->mutex);
- }
- gb_internal void rw_mutex_shared_unlock(RwMutex *m) {
- mutex_unlock(&m->mutex);
- }
- #endif
- struct Parker {
- Futex state;
- };
- enum ParkerState : u32 {
- ParkerState_Empty = 0,
- ParkerState_Notified = 1,
- ParkerState_Parked = UINT32_MAX,
- };
- gb_internal void park(Parker *p) {
- if (p->state.fetch_sub(1, std::memory_order_acquire) == ParkerState_Notified) {
- return;
- }
- for (;;) {
- futex_wait(&p->state, ParkerState_Parked);
- i32 notified = ParkerState_Empty;
- if (p->state.compare_exchange_strong(notified, ParkerState_Empty, std::memory_order_acquire, std::memory_order_acquire)) {
- return;
- }
- }
- }
- gb_internal void unpark_one(Parker *p) {
- if (p->state.exchange(ParkerState_Notified, std::memory_order_release) == ParkerState_Parked) {
- futex_signal(&p->state);
- }
- }
- gb_internal void unpark_all(Parker *p) {
- if (p->state.exchange(ParkerState_Notified, std::memory_order_release) == ParkerState_Parked) {
- futex_broadcast(&p->state);
- }
- }
- gb_internal u32 thread_current_id(void) {
- u32 thread_id;
- #if defined(GB_SYSTEM_WINDOWS)
- #if defined(GB_ARCH_32_BIT) && defined(GB_CPU_X86)
- thread_id = (cast(u32 *)__readfsdword(24))[9];
- #elif defined(GB_ARCH_64_BIT) && defined(GB_CPU_X86)
- thread_id = (cast(u32 *)__readgsqword(48))[18];
- #else
- thread_id = GetCurrentThreadId();
- #endif
- #elif defined(GB_SYSTEM_OSX) && defined(GB_ARCH_64_BIT)
- thread_id = pthread_mach_thread_np(pthread_self());
- #elif defined(GB_ARCH_32_BIT) && defined(GB_CPU_X86)
- __asm__("mov %%gs:0x08,%0" : "=r"(thread_id));
- #elif defined(GB_ARCH_64_BIT) && defined(GB_CPU_X86)
- __asm__("mov %%fs:0x10,%0" : "=r"(thread_id));
- #elif defined(GB_SYSTEM_LINUX)
- thread_id = gettid();
- #elif defined(GB_SYSTEM_HAIKU)
- thread_id = find_thread(NULL);
- #elif defined(GB_SYSTEM_FREEBSD)
- thread_id = pthread_getthreadid_np();
- #elif defined(GB_SYSTEM_NETBSD)
- thread_id = (u32)_lwp_self();
- #else
- #error Unsupported architecture for thread_current_id()
- #endif
- return thread_id;
- }
- gb_internal gb_inline void yield_thread(void) {
- #if defined(GB_SYSTEM_WINDOWS)
- _mm_pause();
- #elif defined(GB_SYSTEM_OSX)
- #if defined(GB_CPU_X86)
- __asm__ volatile ("" : : : "memory");
- #elif defined(GB_CPU_ARM)
- __asm__ volatile ("yield" : : : "memory");
- #endif
- #elif defined(GB_CPU_X86)
- _mm_pause();
- #elif defined(GB_CPU_ARM)
- __asm__ volatile ("yield" : : : "memory");
- #else
- #error Unknown architecture
- #endif
- }
- gb_internal gb_inline void yield(void) {
- #if defined(GB_SYSTEM_WINDOWS)
- YieldProcessor();
- #else
- sched_yield();
- #endif
- }
- #if defined(GB_SYSTEM_WINDOWS)
- gb_internal DWORD __stdcall internal_thread_proc(void *arg) {
- Thread *t = cast(Thread *)arg;
- thread_pool_thread_proc(t);
- return 0;
- }
- #else
- gb_internal void *internal_thread_proc(void *arg) {
- #if (GB_SYSTEM_LINUX)
- // NOTE: Don't permit any signal delivery to threads on Linux.
- sigset_t mask = {};
- sigfillset(&mask);
- GB_ASSERT_MSG(pthread_sigmask(SIG_BLOCK, &mask, nullptr) == 0, "failed to block signals");
- #endif
- Thread *t = cast(Thread *)arg;
- thread_pool_thread_proc(t);
- return NULL;
- }
- #endif
- TaskRingBuffer *taskring_init(isize size) {
- TaskRingBuffer *ring = (TaskRingBuffer *)gb_alloc(heap_allocator(), sizeof(TaskRingBuffer));
- ring->size = size;
- ring->buffer = (WorkerTask *)gb_alloc_array(heap_allocator(), WorkerTask, ring->size);
- return ring;
- }
- void thread_queue_destroy(TaskQueue *q) {
- gb_free(heap_allocator(), (*q->ring).buffer);
- gb_free(heap_allocator(), q->ring);
- }
- gb_internal void thread_init(ThreadPool *pool, Thread *t, isize idx) {
- gb_zero_item(t);
- #if defined(GB_SYSTEM_WINDOWS)
- t->win32_handle = INVALID_HANDLE_VALUE;
- #else
- t->posix_handle = 0;
- #endif
- // Size must be a power of 2
- t->queue.ring = taskring_init(1 << 14);
- t->pool = pool;
- t->idx = idx;
- }
- gb_internal void thread_init_and_start(ThreadPool *pool, Thread *t, isize idx) {
- thread_init(pool, t, idx);
- isize stack_size = 0;
- #if defined(GB_SYSTEM_WINDOWS)
- t->win32_handle = CreateThread(NULL, stack_size, internal_thread_proc, t, 0, NULL);
- GB_ASSERT_MSG(t->win32_handle != NULL, "CreateThread: GetLastError");
- #else
- {
- pthread_attr_t attr;
- pthread_attr_init(&attr);
- defer (pthread_attr_destroy(&attr));
- pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
- if (stack_size != 0) {
- pthread_attr_setstacksize(&attr, stack_size);
- }
- pthread_create(&t->posix_handle, &attr, internal_thread_proc, t);
- }
- #endif
- }
- gb_internal void thread_join_and_destroy(Thread *t) {
- #if defined(GB_SYSTEM_WINDOWS)
- WaitForSingleObject(t->win32_handle, INFINITE);
- CloseHandle(t->win32_handle);
- t->win32_handle = INVALID_HANDLE_VALUE;
- #else
- pthread_join(t->posix_handle, NULL);
- t->posix_handle = 0;
- #endif
- thread_queue_destroy(&t->queue);
- }
- gb_internal void thread_set_name(Thread *t, char const *name) {
- #if defined(GB_COMPILER_MSVC)
- #pragma pack(push, 8)
- typedef struct {
- DWORD type;
- char const *name;
- DWORD id;
- DWORD flags;
- } gbprivThreadName;
- #pragma pack(pop)
- gbprivThreadName tn;
- tn.type = 0x1000;
- tn.name = name;
- tn.id = GetThreadId(cast(HANDLE)t->win32_handle);
- tn.flags = 0;
- __try {
- RaiseException(0x406d1388, 0, gb_size_of(tn)/4, cast(ULONG_PTR *)&tn);
- } __except(1 /*EXCEPTION_EXECUTE_HANDLER*/) {
- }
- #elif defined(GB_SYSTEM_WINDOWS) && !defined(GB_COMPILER_MSVC)
- // IMPORTANT TODO(bill): Set thread name for GCC/Clang on windows
- return;
- #elif defined(GB_SYSTEM_OSX)
- // TODO(bill): Test if this works
- pthread_setname_np(name);
- #elif defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD)
- pthread_set_name_np(t->posix_handle, name);
- #elif defined(GB_SYSTEM_NETBSD)
- pthread_setname_np(t->posix_handle, "%s", (void*)name);
- #else
- // TODO(bill): Test if this works
- pthread_setname_np(t->posix_handle, name);
- #endif
- }
- #if defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_NETBSD)
- #include <sys/syscall.h>
- #ifdef GB_SYSTEM_LINUX
- #include <linux/futex.h>
- #else
- #include <sys/futex.h>
- #define SYS_futex SYS___futex
- #endif
- gb_internal void futex_signal(Futex *addr) {
- int ret = syscall(SYS_futex, addr, FUTEX_WAKE | FUTEX_PRIVATE_FLAG, 1, NULL, NULL, 0);
- if (ret == -1) {
- perror("Futex wake");
- GB_PANIC("Failed in futex wake!\n");
- }
- }
- gb_internal void futex_broadcast(Futex *addr) {
- int ret = syscall(SYS_futex, addr, FUTEX_WAKE | FUTEX_PRIVATE_FLAG, INT32_MAX, NULL, NULL, 0);
- if (ret == -1) {
- perror("Futex wake");
- GB_PANIC("Failed in futex wake!\n");
- }
- }
- gb_internal void futex_wait(Futex *addr, Footex val) {
- for (;;) {
- int ret = syscall(SYS_futex, addr, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, val, NULL, NULL, 0);
- if (ret == -1) {
- if (errno != EAGAIN) {
- perror("Futex wait");
- GB_PANIC("Failed in futex wait!\n");
- } else {
- return;
- }
- } else if (ret == 0) {
- if (*addr != val) {
- return;
- }
- }
- }
- }
- #elif defined(GB_SYSTEM_FREEBSD)
- #include <sys/types.h>
- #include <sys/umtx.h>
- gb_internal void futex_signal(Futex *addr) {
- _umtx_op(addr, UMTX_OP_WAKE, 1, 0, 0);
- }
- gb_internal void futex_broadcast(Futex *addr) {
- _umtx_op(addr, UMTX_OP_WAKE, INT32_MAX, 0, 0);
- }
- gb_internal void futex_wait(Futex *addr, Footex val) {
- for (;;) {
- int ret = _umtx_op(addr, UMTX_OP_WAIT_UINT, val, 0, NULL);
- if (ret == -1) {
- if (errno == ETIMEDOUT || errno == EINTR) {
- continue;
- }
- perror("Futex wait");
- GB_PANIC("Failed in futex wait!\n");
- } else if (ret == 0) {
- if (*addr != val) {
- return;
- }
- }
- }
- }
- #elif defined(GB_SYSTEM_OPENBSD)
- #include <sys/futex.h>
- gb_internal void futex_signal(Futex *f) {
- for (;;) {
- int ret = futex((volatile uint32_t *)f, FUTEX_WAKE | FUTEX_PRIVATE_FLAG, 1, NULL, NULL);
- if (ret == -1) {
- if (errno == ETIMEDOUT || errno == EINTR) {
- continue;
- }
- perror("Futex wake");
- GB_PANIC("futex wake fail");
- } else if (ret == 1) {
- return;
- }
- }
- }
- gb_internal void futex_broadcast(Futex *f) {
- for (;;) {
- int ret = futex((volatile uint32_t *)f, FUTEX_WAKE | FUTEX_PRIVATE_FLAG, INT32_MAX, NULL, NULL);
- if (ret == -1) {
- if (errno == ETIMEDOUT || errno == EINTR) {
- continue;
- }
- perror("Futex wake");
- GB_PANIC("futex wake fail");
- } else if (ret == 1) {
- return;
- }
- }
- }
- gb_internal void futex_wait(Futex *f, Footex val) {
- for (;;) {
- int ret = futex((volatile uint32_t *)f, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, val, NULL, NULL);
- if (ret == -1) {
- if (*f != val) {
- return;
- }
- if (errno == ETIMEDOUT || errno == EINTR) {
- continue;
- }
- perror("Futex wait");
- GB_PANIC("Failed in futex wait!\n");
- }
- }
- }
- #elif defined(GB_SYSTEM_OSX)
- #if __has_include(<os/os_sync_wait_on_address.h>)
- #define DARWIN_WAIT_ON_ADDRESS_AVAILABLE
- #include <os/os_sync_wait_on_address.h>
- #endif
- #define UL_COMPARE_AND_WAIT 0x00000001
- #define ULF_NO_ERRNO 0x01000000
- extern "C" int __ulock_wait(uint32_t operation, void *addr, uint64_t value, uint32_t timeout); /* timeout is specified in microseconds */
- extern "C" int __ulock_wake(uint32_t operation, void *addr, uint64_t wake_value);
- gb_internal void futex_signal(Futex *f) {
- #ifdef DARWIN_WAIT_ON_ADDRESS_AVAILABLE
- if (__builtin_available(macOS 14.4, *)) {
- for (;;) {
- int ret = os_sync_wake_by_address_any(f, sizeof(Futex), OS_SYNC_WAKE_BY_ADDRESS_NONE);
- if (ret >= 0) {
- return;
- }
- if (errno == EINTR || errno == EFAULT) {
- continue;
- }
- if (errno == ENOENT) {
- return;
- }
- GB_PANIC("Failed in futex wake %d %d!\n", ret, errno);
- }
- } else {
- #endif
- for (;;) {
- int ret = __ulock_wake(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, f, 0);
- if (ret >= 0) {
- return;
- }
- if (ret == -EINTR || ret == -EFAULT) {
- continue;
- }
- if (ret == -ENOENT) {
- return;
- }
- GB_PANIC("Failed in futex wake!\n");
- }
- #ifdef DARWIN_WAIT_ON_ADDRESS_AVAILABLE
- }
- #endif
- }
- gb_internal void futex_broadcast(Futex *f) {
- #ifdef DARWIN_WAIT_ON_ADDRESS_AVAILABLE
- if (__builtin_available(macOS 14.4, *)) {
- for (;;) {
- int ret = os_sync_wake_by_address_all(f, sizeof(Footex), OS_SYNC_WAKE_BY_ADDRESS_NONE);
- if (ret >= 0) {
- return;
- }
- if (errno == EINTR || errno == EFAULT) {
- continue;
- }
- if (errno == ENOENT) {
- return;
- }
- GB_PANIC("Failed in futext wake %d %d!\n", ret, errno);
- }
- } else {
- #endif
- for (;;) {
- enum { ULF_WAKE_ALL = 0x00000100 };
- int ret = __ulock_wake(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO | ULF_WAKE_ALL, f, 0);
- if (ret == 0) {
- return;
- }
- if (ret == -EINTR || ret == -EFAULT) {
- continue;
- }
- if (ret == -ENOENT) {
- return;
- }
- GB_PANIC("Failed in futex wake!\n");
- }
- #ifdef DARWIN_WAIT_ON_ADDRESS_AVAILABLE
- }
- #endif
- }
- gb_internal void futex_wait(Futex *f, Footex val) {
- #ifdef DARWIN_WAIT_ON_ADDRESS_AVAILABLE
- if (__builtin_available(macOS 14.4, *)) {
- for (;;) {
- int ret = os_sync_wait_on_address(f, cast(uint64_t)(val), sizeof(Footex), OS_SYNC_WAIT_ON_ADDRESS_NONE);
- if (ret >= 0) {
- if (*f != val) {
- return;
- }
- continue;
- }
- if (errno == EINTR || errno == EFAULT) {
- continue;
- }
- if (errno == ENOENT) {
- return;
- }
- GB_PANIC("Failed in futex wait %d %d!\n", ret, errno);
- }
- } else {
- #endif
- for (;;) {
- int ret = __ulock_wait(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, f, val, 0);
- if (ret >= 0) {
- if (*f != val) {
- return;
- }
- continue;
- }
- if (ret == -EINTR || ret == -EFAULT) {continue;
- ret = -ret;
- }
- if (ret == -ENOENT) {
- return;
- }
- GB_PANIC("Failed in futex wait!\n");
- }
- #ifdef DARWIN_WAIT_ON_ADDRESS_AVAILABLE
- }
- #endif
- }
- #elif defined(GB_SYSTEM_WINDOWS)
- gb_internal void futex_signal(Futex *f) {
- WakeByAddressSingle(f);
- }
- gb_internal void futex_broadcast(Futex *f) {
- WakeByAddressAll(f);
- }
- gb_internal void futex_wait(Futex *f, Footex val) {
- do {
- WaitOnAddress(f, (void *)&val, sizeof(val), INFINITE);
- } while (f->load() == val);
- }
- #elif defined(GB_SYSTEM_HAIKU)
- // Futex implementation taken from https://tavianator.com/2023/futex.html
- #include <pthread.h>
- #include <atomic>
- struct _Spinlock {
- std::atomic_flag state;
- void init() {
- state.clear();
- }
- void lock() {
- while (state.test_and_set(std::memory_order_acquire)) {
- #if defined(GB_CPU_X86)
- _mm_pause();
- #else
- (void)0; // spin...
- #endif
- }
- }
- void unlock() {
- state.clear(std::memory_order_release);
- }
- };
- struct Futex_Waitq;
-
- struct Futex_Waiter {
- _Spinlock lock;
- pthread_t thread;
- Futex *futex;
- Futex_Waitq *waitq;
- Futex_Waiter *prev, *next;
- };
-
- struct Futex_Waitq {
- _Spinlock lock;
- Futex_Waiter list;
-
- void init() {
- auto head = &list;
- head->prev = head->next = head;
- }
- };
- // FIXME: This approach may scale badly in the future,
- // possible solution - hash map (leads to deadlocks now).
-
- Futex_Waitq g_waitq = {
- .lock = ATOMIC_FLAG_INIT,
- .list = {
- .prev = &g_waitq.list,
- .next = &g_waitq.list,
- },
- };
-
- Futex_Waitq *get_waitq(Futex *f) {
- // Future hash map method...
- return &g_waitq;
- }
-
- void futex_signal(Futex *f) {
- auto waitq = get_waitq(f);
-
- waitq->lock.lock();
-
- auto head = &waitq->list;
- for (auto waiter = head->next; waiter != head; waiter = waiter->next) {
- if (waiter->futex != f) {
- continue;
- }
- waitq->lock.unlock();
- pthread_kill(waiter->thread, SIGCONT);
- return;
- }
-
- waitq->lock.unlock();
- }
-
- void futex_broadcast(Futex *f) {
- auto waitq = get_waitq(f);
-
- waitq->lock.lock();
-
- auto head = &waitq->list;
- for (auto waiter = head->next; waiter != head; waiter = waiter->next) {
- if (waiter->futex != f) {
- continue;
- }
- if (waiter->next == head) {
- waitq->lock.unlock();
- pthread_kill(waiter->thread, SIGCONT);
- return;
- } else {
- pthread_kill(waiter->thread, SIGCONT);
- }
- }
-
- waitq->lock.unlock();
- }
-
- void futex_wait(Futex *f, Footex val) {
- Futex_Waiter waiter;
- waiter.thread = pthread_self();
- waiter.futex = f;
- auto waitq = get_waitq(f);
- while (waitq->lock.state.test_and_set(std::memory_order_acquire)) {
- if (f->load(std::memory_order_relaxed) != val) {
- return;
- }
- #if defined(GB_CPU_X86)
- _mm_pause();
- #else
- (void)0; // spin...
- #endif
- }
- waiter.waitq = waitq;
- waiter.lock.init();
- waiter.lock.lock();
-
- auto head = &waitq->list;
- waiter.prev = head->prev;
- waiter.next = head;
- waiter.prev->next = &waiter;
- waiter.next->prev = &waiter;
-
- waiter.prev->next = &waiter;
- waiter.next->prev = &waiter;
-
- sigset_t old_mask, mask;
- sigemptyset(&mask);
- sigaddset(&mask, SIGCONT);
- pthread_sigmask(SIG_BLOCK, &mask, &old_mask);
- if (f->load(std::memory_order_relaxed) == val) {
- waiter.lock.unlock();
- waitq->lock.unlock();
- int sig;
- sigwait(&mask, &sig);
- waitq->lock.lock();
- waiter.lock.lock();
- while (waitq != waiter.waitq) {
- auto req = waiter.waitq;
- waiter.lock.unlock();
- waitq->lock.unlock();
- waitq = req;
- waitq->lock.lock();
- waiter.lock.lock();
- }
- }
-
- waiter.prev->next = waiter.next;
- waiter.next->prev = waiter.prev;
-
- pthread_sigmask(SIG_SETMASK, &old_mask, NULL);
-
- waiter.lock.unlock();
- waitq->lock.unlock();
- }
- #endif
- #if defined(GB_SYSTEM_WINDOWS)
- #pragma warning(pop)
- #endif
|