aboutsummaryrefslogtreecommitdiff
path: root/src/threading.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/threading.cpp')
-rw-r--r--src/threading.cpp348
1 files changed, 348 insertions, 0 deletions
diff --git a/src/threading.cpp b/src/threading.cpp
new file mode 100644
index 000000000..803cdb662
--- /dev/null
+++ b/src/threading.cpp
@@ -0,0 +1,348 @@
+struct BlockingMutex;
+struct RecursiveMutex;
+struct Semaphore;
+struct Thread;
+
+#define THREAD_PROC(name) isize name(struct Thread *thread)
+typedef THREAD_PROC(ThreadProc);
+
+struct Thread {
+#if defined(GB_SYSTEM_WINDOWS)
+ void * win32_handle;
+#else
+ pthread_t posix_handle;
+#endif
+
+ ThreadProc * proc;
+ void * user_data;
+ isize user_index;
+ isize volatile return_value;
+
+ Semaphore *semaphore;
+ isize stack_size;
+ b32 volatile is_running;
+};
+
+
+void mutex_init (BlockingMutex *m);
+void mutex_destroy (BlockingMutex *m);
+void mutex_lock (BlockingMutex *m);
+bool mutex_try_lock(BlockingMutex *m);
+void mutex_unlock (BlockingMutex *m);
+void mutex_init (RecursiveMutex *m);
+void mutex_destroy (RecursiveMutex *m);
+void mutex_lock (RecursiveMutex *m);
+bool mutex_try_lock(RecursiveMutex *m);
+void mutex_unlock (RecursiveMutex *m);
+
+void semaphore_init (Semaphore *s);
+void semaphore_destroy(Semaphore *s);
+void semaphore_post (Semaphore *s, i32 count);
+void semaphore_wait (Semaphore *s);
+void semaphore_release(Semaphore *s) { semaphore_post(s, 1); }
+
+u32 thread_current_id(void);
+
+void thread_init (Thread *t);
+void thread_destroy (Thread *t);
+void thread_start (Thread *t, ThreadProc *proc, void *data);
+void thread_start_with_stack(Thread *t, ThreadProc *proc, void *data, isize stack_size);
+void thread_join (Thread *t);
+bool thread_is_running (Thread const *t);
+void thread_set_name (Thread *t, char const *name);
+
+void yield_thread(void);
+void yield_process(void);
+
+
+#if defined(GB_SYSTEM_WINDOWS)
+ struct BlockingMutex {
+ SRWLOCK srwlock;
+ };
+ void mutex_init(BlockingMutex *m) {
+ }
+ void mutex_destroy(BlockingMutex *m) {
+ }
+ void mutex_lock(BlockingMutex *m) {
+ AcquireSRWLockExclusive(&m->srwlock);
+ }
+ bool mutex_try_lock(BlockingMutex *m) {
+ return !!TryAcquireSRWLockExclusive(&m->srwlock);
+ }
+ void mutex_unlock(BlockingMutex *m) {
+ ReleaseSRWLockExclusive(&m->srwlock);
+ }
+
+ struct RecursiveMutex {
+ CRITICAL_SECTION win32_critical_section;
+ };
+ void mutex_init(RecursiveMutex *m) {
+ InitializeCriticalSection(&m->win32_critical_section);
+ }
+ void mutex_destroy(RecursiveMutex *m) {
+ DeleteCriticalSection(&m->win32_critical_section);
+ }
+ void mutex_lock(RecursiveMutex *m) {
+ EnterCriticalSection(&m->win32_critical_section);
+ }
+ bool mutex_try_lock(RecursiveMutex *m) {
+ return TryEnterCriticalSection(&m->win32_critical_section) != 0;
+ }
+ void mutex_unlock(RecursiveMutex *m) {
+ LeaveCriticalSection(&m->win32_critical_section);
+ }
+
+ struct Semaphore {
+ void *win32_handle;
+ };
+
+ void semaphore_init(Semaphore *s) {
+ s->win32_handle = CreateSemaphoreA(NULL, 0, I32_MAX, NULL);
+ }
+ void semaphore_destroy(Semaphore *s) {
+ CloseHandle(s->win32_handle);
+ }
+ void semaphore_post(Semaphore *s, i32 count) {
+ ReleaseSemaphore(s->win32_handle, count, NULL);
+ }
+ void semaphore_wait(Semaphore *s) {
+ WaitForSingleObjectEx(s->win32_handle, INFINITE, FALSE);
+ }
+
+#else
+ struct BlockingMutex {
+ pthread_mutex_t pthread_mutex;
+ };
+ void mutex_init(BlockingMutex *m) {
+ pthread_mutex_init(&m->pthread_mutex, nullptr);
+ }
+ void mutex_destroy(BlockingMutex *m) {
+ pthread_mutex_destroy(&m->pthread_mutex);
+ }
+ void mutex_lock(BlockingMutex *m) {
+ pthread_mutex_lock(&m->pthread_mutex);
+ }
+ bool mutex_try_lock(BlockingMutex *m) {
+ return pthread_mutex_trylock(&m->pthread_mutex) == 0;
+ }
+ void mutex_unlock(BlockingMutex *m) {
+ pthread_mutex_unlock(&m->pthread_mutex);
+ }
+
+ struct RecursiveMutex {
+ pthread_mutex_t pthread_mutex;
+ pthread_mutexattr_t pthread_mutexattr;
+ };
+ void mutex_init(RecursiveMutex *m) {
+ pthread_mutexattr_init(&m->pthread_mutexattr);
+ pthread_mutexattr_settype(&m->pthread_mutexattr, PTHREAD_MUTEX_RECURSIVE);
+ pthread_mutex_init(&m->pthread_mutex, &m->pthread_mutexattr);
+ }
+ void mutex_destroy(RecursiveMutex *m) {
+ pthread_mutex_destroy(&m->pthread_mutex);
+ }
+ void mutex_lock(RecursiveMutex *m) {
+ pthread_mutex_lock(&m->pthread_mutex);
+ }
+ bool mutex_try_lock(RecursiveMutex *m) {
+ return pthread_mutex_trylock(&m->pthread_mutex) == 0;
+ }
+ void mutex_unlock(RecursiveMutex *m) {
+ pthread_mutex_unlock(&m->pthread_mutex);
+ }
+
+ #if defined(GB_SYSTEM_OSX)
+ struct Semaphore {
+ semaphore_t osx_handle;
+ };
+
+ void semaphore_init (Semaphore *s) { semaphore_create(mach_task_self(), &s->osx_handle, SYNC_POLICY_FIFO, 0); }
+ void semaphore_destroy(Semaphore *s) { semaphore_destroy(mach_task_self(), s->osx_handle); }
+ void semaphore_post (Semaphore *s, i32 count) { while (count --> 0) semaphore_signal(s->osx_handle); }
+ void semaphore_wait (Semaphore *s) { semaphore_wait(s->osx_handle); }
+ #elif defined(GB_SYSTEM_UNIX)
+ struct Semaphore {
+ sem_t unix_handle;
+ };
+
+ void semaphore_init (Semaphore *s) { sem_init(&s->unix_handle, 0, 0); }
+ void semaphore_destroy(Semaphore *s) { sem_destroy(&s->unix_handle); }
+ void semaphore_post (Semaphore *s, i32 count) { while (count --> 0) sem_post(&s->unix_handle); }
+ void semaphore_wait (Semaphore *s) { int i; do { i = sem_wait(&s->unix_handle); } while (i == -1 && errno == EINTR); }
+ #else
+ #error
+ #endif
+#endif
+
+
+
+
+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));
+#else
+ #error Unsupported architecture for thread_current_id()
+#endif
+
+ return thread_id;
+}
+
+
+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();
+#else
+#error Unknown architecture
+#endif
+}
+
+gb_inline void yield(void) {
+#if defined(GB_SYSTEM_WINDOWS)
+ YieldProcessor();
+#else
+ sched_yield();
+#endif
+}
+
+
+void thread_init(Thread *t) {
+ gb_zero_item(t);
+#if defined(GB_SYSTEM_WINDOWS)
+ t->win32_handle = INVALID_HANDLE_VALUE;
+#else
+ t->posix_handle = 0;
+#endif
+ t->semaphore = gb_alloc_item(heap_allocator(), Semaphore);
+ semaphore_init(t->semaphore);
+}
+
+void thread_destroy(Thread *t) {
+ if (t->is_running) thread_join(t);
+ semaphore_destroy(t->semaphore);
+ gb_free(heap_allocator(), t->semaphore);
+}
+
+
+void gb__thread_run(Thread *t) {
+ semaphore_release(t->semaphore);
+ t->return_value = t->proc(t);
+}
+
+#if defined(GB_SYSTEM_WINDOWS)
+ DWORD __stdcall internal_thread_proc(void *arg) {
+ Thread *t = cast(Thread *)arg;
+ gb__thread_run(t);
+ t->is_running = false;
+ return 0;
+ }
+#else
+ void * internal_thread_proc(void *arg) {
+ Thread *t = cast(Thread *)arg;
+ gb__thread_run(t);
+ t->is_running = false;
+ return NULL;
+ }
+#endif
+
+void thread_start(Thread *t, ThreadProc *proc, void *user_data) { thread_start_with_stack(t, proc, user_data, 0); }
+
+void thread_start_with_stack(Thread *t, ThreadProc *proc, void *user_data, isize stack_size) {
+ GB_ASSERT(!t->is_running);
+ GB_ASSERT(proc != NULL);
+ t->proc = proc;
+ t->user_data = user_data;
+ t->stack_size = stack_size;
+ t->is_running = true;
+
+#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);
+ 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);
+ pthread_attr_destroy(&attr);
+ }
+#endif
+
+ semaphore_wait(t->semaphore);
+}
+
+void thread_join(Thread *t) {
+ if (!t->is_running) return;
+
+#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
+ t->is_running = false;
+}
+
+bool thread_is_running(Thread const *t) { return t->is_running != 0; }
+
+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)
+ pthread_set_name_np(t->posix_handle, name);
+#else
+ // TODO(bill): Test if this works
+ pthread_setname_np(t->posix_handle, name);
+#endif
+}
+