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