master ohmu/fastfetch-openbsd / src / common / thread.h
ohmu  ·  2026-10-15
  1#pragma once
  2
  3#include "fastfetch.h"
  4
  5#ifdef FF_HAVE_THREADS
  6    #if defined(_WIN32)
  7        #include "common/windows/nt.h"
  8        #include <synchapi.h>
  9        #include <process.h>
 10        #include <processthreadsapi.h>
 11        #define FF_THREAD_MUTEX_INITIALIZER SRWLOCK_INIT
 12typedef SRWLOCK FFThreadMutex;
 13typedef HANDLE FFThreadType;
 14[[gnu::nonnull(1)]] static inline void ffThreadMutexLock(FFThreadMutex* mutex) {
 15    AcquireSRWLockExclusive(mutex);
 16}
 17[[gnu::nonnull(1)]] static inline void ffThreadMutexUnlock(FFThreadMutex* mutex) {
 18    ReleaseSRWLockExclusive(mutex);
 19}
 20// `data` is the thread argument and may be null; `func` may not
 21[[gnu::nonnull(1)]] static inline FFThreadType ffThreadCreate(unsigned(__stdcall* func)(void*), void* data) {
 22    return (FFThreadType) _beginthreadex(nullptr, 0, func, data, 0, nullptr);
 23}
 24        #define FF_THREAD_ENTRY_DECL_WRAPPER(fn, paramType)        \
 25            static __stdcall unsigned fn##ThreadMain(void* data) { \
 26                fn((paramType) data);                              \
 27                return 0;                                          \
 28            }
 29        #define FF_THREAD_ENTRY_DECL_WRAPPER_NOPARAM(fn) \
 30            static __stdcall unsigned fn##ThreadMain() { \
 31                fn();                                    \
 32                return 0;                                \
 33            }
 34static inline void ffThreadDetach(FFThreadType thread) {
 35    NtClose(thread);
 36}
 37static inline bool ffThreadJoin(FFThreadType thread, uint32_t timeout) {
 38    if (NtWaitForSingleObject(thread, FALSE, timeout == 0 ? nullptr : &(LARGE_INTEGER) { .QuadPart = (int64_t) timeout * -10000 }) != STATUS_WAIT_0) {
 39        TerminateThread(thread, (DWORD) -1);
 40        NtClose(thread);
 41        return false;
 42    }
 43    NtClose(thread);
 44    return true;
 45}
 46static inline uintptr_t ffThreadGetCurrentId() {
 47    return (uintptr_t) ffGetTeb()->ClientId.UniqueThread;
 48}
 49    #else
 50        #include <pthread.h>
 51        #include <signal.h>
 52        #if FF_HAVE_PTHREAD_NP
 53            #include <pthread_np.h>
 54        #endif
 55typedef pthread_t FFThreadType;
 56        #if __APPLE__
 57            #include <os/lock.h>
 58            #define FF_THREAD_MUTEX_INITIALIZER OS_UNFAIR_LOCK_INIT
 59typedef os_unfair_lock FFThreadMutex;
 60[[gnu::nonnull(1)]] static inline void ffThreadMutexLock(os_unfair_lock* mutex) {
 61    os_unfair_lock_lock(mutex);
 62}
 63[[gnu::nonnull(1)]] static inline void ffThreadMutexUnlock(os_unfair_lock* mutex) {
 64    os_unfair_lock_unlock(mutex);
 65}
 66        #else
 67            #define FF_THREAD_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
 68typedef pthread_mutex_t FFThreadMutex;
 69[[gnu::nonnull(1)]] static inline void ffThreadMutexLock(FFThreadMutex* mutex) {
 70    pthread_mutex_lock(mutex);
 71}
 72[[gnu::nonnull(1)]] static inline void ffThreadMutexUnlock(FFThreadMutex* mutex) {
 73    pthread_mutex_unlock(mutex);
 74}
 75        #endif
 76// `data` is the thread argument and may be null; `func` may not
 77[[gnu::nonnull(1)]] static inline FFThreadType ffThreadCreate(void* (*func)(void*), void* data) {
 78    FFThreadType newThread = 0;
 79    pthread_create(&newThread, nullptr, func, data);
 80    return newThread;
 81}
 82        #define FF_THREAD_ENTRY_DECL_WRAPPER(fn, paramType) \
 83            static void* fn##ThreadMain(void* data) {       \
 84                fn((paramType) data);                       \
 85                return nullptr;                                \
 86            }
 87        #define FF_THREAD_ENTRY_DECL_WRAPPER_NOPARAM(fn) \
 88            static void* fn##ThreadMain() {              \
 89                fn();                                    \
 90                return nullptr;                             \
 91            }
 92static inline void ffThreadDetach(FFThreadType thread) {
 93    pthread_detach(thread);
 94}
 95static inline bool ffThreadJoin(FFThreadType thread, [[maybe_unused]] uint32_t timeout) {
 96        #if HAVE_TIMEDJOIN_NP
 97    if (timeout > 0) {
 98        struct timespec ts;
 99        if (clock_gettime(CLOCK_REALTIME, &ts) == 0) {
100            ts.tv_sec += timeout / 1000;
101            ts.tv_nsec += (timeout % 1000) * 1000000;
102            if (pthread_timedjoin_np(thread, nullptr, &ts) != 0) {
103                pthread_kill(thread, SIGTERM);
104                return false;
105            }
106            return true;
107        }
108    }
109        #endif
110    pthread_join(thread, nullptr);
111    return true;
112}
113static inline uintptr_t ffThreadGetCurrentId() {
114    return (uintptr_t) pthread_self();
115}
116    #endif
117#else // FF_HAVE_THREADS
118    #define FF_THREAD_MUTEX_INITIALIZER 0
119typedef char FFThreadMutex;
120// The stubs deliberately ignore their argument, so no `nonnull` here
121static inline void ffThreadMutexLock([[maybe_unused]] FFThreadMutex* mutex) {}
122static inline void ffThreadMutexUnlock([[maybe_unused]] FFThreadMutex* mutex) {}
123    #define FF_THREAD_ENTRY_DECL_WRAPPER(fn, paramType)
124#endif // FF_HAVE_THREADS