mythread.h 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  1. // SPDX-License-Identifier: 0BSD
  2. ///////////////////////////////////////////////////////////////////////////////
  3. //
  4. /// \file mythread.h
  5. /// \brief Some threading related helper macros and functions
  6. //
  7. // Author: Lasse Collin
  8. //
  9. ///////////////////////////////////////////////////////////////////////////////
  10. #ifndef MYTHREAD_H
  11. #define MYTHREAD_H
  12. #include "sysdefs.h"
  13. // If any type of threading is enabled, #define MYTHREAD_ENABLED.
  14. #if defined(MYTHREAD_POSIX) || defined(MYTHREAD_WIN95) \
  15. || defined(MYTHREAD_VISTA)
  16. # define MYTHREAD_ENABLED 1
  17. #endif
  18. #ifdef MYTHREAD_ENABLED
  19. ////////////////////////////////////////
  20. // Shared between all threading types //
  21. ////////////////////////////////////////
  22. // Locks a mutex for a duration of a block.
  23. //
  24. // Perform mythread_mutex_lock(&mutex) in the beginning of a block
  25. // and mythread_mutex_unlock(&mutex) at the end of the block. "break"
  26. // may be used to unlock the mutex and jump out of the block.
  27. // mythread_sync blocks may be nested.
  28. //
  29. // Example:
  30. //
  31. // mythread_sync(mutex) {
  32. // foo();
  33. // if (some_error)
  34. // break; // Skips bar()
  35. // bar();
  36. // }
  37. //
  38. // At least GCC optimizes the loops completely away so it doesn't slow
  39. // things down at all compared to plain mythread_mutex_lock(&mutex)
  40. // and mythread_mutex_unlock(&mutex) calls.
  41. //
  42. #define mythread_sync(mutex) mythread_sync_helper1(mutex, __LINE__)
  43. #define mythread_sync_helper1(mutex, line) mythread_sync_helper2(mutex, line)
  44. #define mythread_sync_helper2(mutex, line) \
  45. for (unsigned int mythread_i_ ## line = 0; \
  46. mythread_i_ ## line \
  47. ? (mythread_mutex_unlock(&(mutex)), 0) \
  48. : (mythread_mutex_lock(&(mutex)), 1); \
  49. mythread_i_ ## line = 1) \
  50. for (unsigned int mythread_j_ ## line = 0; \
  51. !mythread_j_ ## line; \
  52. mythread_j_ ## line = 1)
  53. #endif
  54. #if !defined(MYTHREAD_ENABLED)
  55. //////////////////
  56. // No threading //
  57. //////////////////
  58. // Calls the given function once. This isn't thread safe.
  59. #define mythread_once(func) \
  60. do { \
  61. static bool once_ = false; \
  62. if (!once_) { \
  63. func(); \
  64. once_ = true; \
  65. } \
  66. } while (0)
  67. #if !(defined(_WIN32) && !defined(__CYGWIN__)) && !defined(__wasm__)
  68. // Use sigprocmask() to set the signal mask in single-threaded programs.
  69. #include <signal.h>
  70. static inline void
  71. mythread_sigmask(int how, const sigset_t *restrict set,
  72. sigset_t *restrict oset)
  73. {
  74. int ret = sigprocmask(how, set, oset);
  75. assert(ret == 0);
  76. (void)ret;
  77. }
  78. #endif
  79. #elif defined(MYTHREAD_POSIX)
  80. ////////////////////
  81. // Using pthreads //
  82. ////////////////////
  83. #include <pthread.h>
  84. #include <signal.h>
  85. #include <time.h>
  86. #include <errno.h>
  87. // If clock_gettime() isn't available, use gettimeofday() from <sys/time.h>
  88. // as a fallback. gettimeofday() is in SUSv2 and thus is supported on all
  89. // relevant POSIX systems.
  90. #ifndef HAVE_CLOCK_GETTIME
  91. # include <sys/time.h>
  92. #endif
  93. // MinGW-w64 with winpthreads:
  94. //
  95. // NOTE: Typical builds with MinGW-w64 don't use this code (MYTHREAD_POSIX).
  96. // Instead, native Windows threading APIs are used (MYTHREAD_VISTA or
  97. // MYTHREAD_WIN95).
  98. //
  99. // MinGW-w64 has _sigset_t (an integer type) in <sys/types.h>.
  100. // If _POSIX was #defined, the header would add the alias sigset_t too.
  101. // Let's keep this working even without _POSIX.
  102. //
  103. // There are no functions that actually do something with sigset_t
  104. // because signals barely exist on Windows. The sigfillset macro below
  105. // is just to silence warnings. There is no sigfillset() in MinGW-w64.
  106. #ifdef __MINGW32__
  107. # include <sys/types.h>
  108. # define sigset_t _sigset_t
  109. # define sigfillset(set_ptr) do { *(set_ptr) = 0; } while (0)
  110. #endif
  111. #define MYTHREAD_RET_TYPE void *
  112. #define MYTHREAD_RET_VALUE NULL
  113. typedef pthread_t mythread;
  114. typedef pthread_mutex_t mythread_mutex;
  115. typedef struct {
  116. pthread_cond_t cond;
  117. #ifdef HAVE_CLOCK_GETTIME
  118. // Clock ID (CLOCK_REALTIME or CLOCK_MONOTONIC) associated with
  119. // the condition variable.
  120. clockid_t clk_id;
  121. #endif
  122. } mythread_cond;
  123. typedef struct timespec mythread_condtime;
  124. // Calls the given function once in a thread-safe way.
  125. #define mythread_once(func) \
  126. do { \
  127. static pthread_once_t once_ = PTHREAD_ONCE_INIT; \
  128. pthread_once(&once_, &func); \
  129. } while (0)
  130. // Use pthread_sigmask() to set the signal mask in multi-threaded programs.
  131. // Do nothing on OpenVMS since it lacks pthread_sigmask().
  132. // Do nothing on MinGW-w64 too to silence warnings (its pthread_sigmask()
  133. // is #defined to 0 so it's a no-op).
  134. static inline void
  135. mythread_sigmask(int how, const sigset_t *restrict set,
  136. sigset_t *restrict oset)
  137. {
  138. #if defined(__VMS) || defined(__MINGW32__)
  139. (void)how;
  140. (void)set;
  141. (void)oset;
  142. #else
  143. int ret = pthread_sigmask(how, set, oset);
  144. assert(ret == 0);
  145. (void)ret;
  146. #endif
  147. }
  148. // Creates a new thread with all signals blocked. Returns zero on success
  149. // and non-zero on error.
  150. static inline int
  151. mythread_create(mythread *thread, void *(*func)(void *arg), void *arg)
  152. {
  153. sigset_t old;
  154. sigset_t all;
  155. sigfillset(&all);
  156. mythread_sigmask(SIG_SETMASK, &all, &old);
  157. const int ret = pthread_create(thread, NULL, func, arg);
  158. mythread_sigmask(SIG_SETMASK, &old, NULL);
  159. return ret;
  160. }
  161. // Joins a thread. Returns zero on success and non-zero on error.
  162. static inline int
  163. mythread_join(mythread thread)
  164. {
  165. return pthread_join(thread, NULL);
  166. }
  167. // Initializes a mutex. Returns zero on success and non-zero on error.
  168. static inline int
  169. mythread_mutex_init(mythread_mutex *mutex)
  170. {
  171. return pthread_mutex_init(mutex, NULL);
  172. }
  173. static inline void
  174. mythread_mutex_destroy(mythread_mutex *mutex)
  175. {
  176. int ret = pthread_mutex_destroy(mutex);
  177. assert(ret == 0);
  178. (void)ret;
  179. }
  180. static inline void
  181. mythread_mutex_lock(mythread_mutex *mutex)
  182. {
  183. int ret = pthread_mutex_lock(mutex);
  184. assert(ret == 0);
  185. (void)ret;
  186. }
  187. static inline void
  188. mythread_mutex_unlock(mythread_mutex *mutex)
  189. {
  190. int ret = pthread_mutex_unlock(mutex);
  191. assert(ret == 0);
  192. (void)ret;
  193. }
  194. // Initializes a condition variable.
  195. //
  196. // Using CLOCK_MONOTONIC instead of the default CLOCK_REALTIME makes the
  197. // timeout in pthread_cond_timedwait() work correctly also if system time
  198. // is suddenly changed. Unfortunately CLOCK_MONOTONIC isn't available
  199. // everywhere while the default CLOCK_REALTIME is, so the default is
  200. // used if CLOCK_MONOTONIC isn't available.
  201. //
  202. // If clock_gettime() isn't available at all, gettimeofday() will be used.
  203. static inline int
  204. mythread_cond_init(mythread_cond *mycond)
  205. {
  206. #ifdef HAVE_CLOCK_GETTIME
  207. # if defined(HAVE_PTHREAD_CONDATTR_SETCLOCK) && \
  208. defined(HAVE_CLOCK_MONOTONIC)
  209. struct timespec ts;
  210. pthread_condattr_t condattr;
  211. // POSIX doesn't seem to *require* that pthread_condattr_setclock()
  212. // will fail if given an unsupported clock ID. Test that
  213. // CLOCK_MONOTONIC really is supported using clock_gettime().
  214. if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0
  215. && pthread_condattr_init(&condattr) == 0) {
  216. int ret = pthread_condattr_setclock(
  217. &condattr, CLOCK_MONOTONIC);
  218. if (ret == 0)
  219. ret = pthread_cond_init(&mycond->cond, &condattr);
  220. pthread_condattr_destroy(&condattr);
  221. if (ret == 0) {
  222. mycond->clk_id = CLOCK_MONOTONIC;
  223. return 0;
  224. }
  225. }
  226. // If anything above fails, fall back to the default CLOCK_REALTIME.
  227. // POSIX requires that all implementations of clock_gettime() must
  228. // support at least CLOCK_REALTIME.
  229. # endif
  230. mycond->clk_id = CLOCK_REALTIME;
  231. #endif
  232. return pthread_cond_init(&mycond->cond, NULL);
  233. }
  234. static inline void
  235. mythread_cond_destroy(mythread_cond *cond)
  236. {
  237. int ret = pthread_cond_destroy(&cond->cond);
  238. assert(ret == 0);
  239. (void)ret;
  240. }
  241. static inline void
  242. mythread_cond_signal(mythread_cond *cond)
  243. {
  244. int ret = pthread_cond_signal(&cond->cond);
  245. assert(ret == 0);
  246. (void)ret;
  247. }
  248. static inline void
  249. mythread_cond_wait(mythread_cond *cond, mythread_mutex *mutex)
  250. {
  251. int ret = pthread_cond_wait(&cond->cond, mutex);
  252. assert(ret == 0);
  253. (void)ret;
  254. }
  255. // Waits on a condition or until a timeout expires. If the timeout expires,
  256. // non-zero is returned, otherwise zero is returned.
  257. static inline int
  258. mythread_cond_timedwait(mythread_cond *cond, mythread_mutex *mutex,
  259. const mythread_condtime *condtime)
  260. {
  261. int ret = pthread_cond_timedwait(&cond->cond, mutex, condtime);
  262. assert(ret == 0 || ret == ETIMEDOUT);
  263. return ret;
  264. }
  265. // Sets condtime to the absolute time that is timeout_ms milliseconds
  266. // in the future. The type of the clock to use is taken from cond.
  267. static inline void
  268. mythread_condtime_set(mythread_condtime *condtime, const mythread_cond *cond,
  269. uint32_t timeout_ms)
  270. {
  271. condtime->tv_sec = (time_t)(timeout_ms / 1000);
  272. condtime->tv_nsec = (long)((timeout_ms % 1000) * 1000000);
  273. #ifdef HAVE_CLOCK_GETTIME
  274. struct timespec now;
  275. int ret = clock_gettime(cond->clk_id, &now);
  276. assert(ret == 0);
  277. (void)ret;
  278. condtime->tv_sec += now.tv_sec;
  279. condtime->tv_nsec += now.tv_nsec;
  280. #else
  281. (void)cond;
  282. struct timeval now;
  283. gettimeofday(&now, NULL);
  284. condtime->tv_sec += now.tv_sec;
  285. condtime->tv_nsec += now.tv_usec * 1000L;
  286. #endif
  287. // tv_nsec must stay in the range [0, 999_999_999].
  288. if (condtime->tv_nsec >= 1000000000L) {
  289. condtime->tv_nsec -= 1000000000L;
  290. ++condtime->tv_sec;
  291. }
  292. }
  293. #elif defined(MYTHREAD_WIN95) || defined(MYTHREAD_VISTA)
  294. /////////////////////
  295. // Windows threads //
  296. /////////////////////
  297. #define WIN32_LEAN_AND_MEAN
  298. #ifdef MYTHREAD_VISTA
  299. # undef _WIN32_WINNT
  300. # define _WIN32_WINNT 0x0600
  301. #endif
  302. #include <windows.h>
  303. #include <process.h>
  304. #define MYTHREAD_RET_TYPE unsigned int __stdcall
  305. #define MYTHREAD_RET_VALUE 0
  306. typedef HANDLE mythread;
  307. typedef CRITICAL_SECTION mythread_mutex;
  308. #ifdef MYTHREAD_WIN95
  309. typedef HANDLE mythread_cond;
  310. #else
  311. typedef CONDITION_VARIABLE mythread_cond;
  312. #endif
  313. typedef struct {
  314. // Tick count (milliseconds) in the beginning of the timeout.
  315. // NOTE: This is 32 bits so it wraps around after 49.7 days.
  316. // Multi-day timeouts may not work as expected.
  317. DWORD start;
  318. // Length of the timeout in milliseconds. The timeout expires
  319. // when the current tick count minus "start" is equal or greater
  320. // than "timeout".
  321. DWORD timeout;
  322. } mythread_condtime;
  323. // mythread_once() is only available with Vista threads.
  324. #ifdef MYTHREAD_VISTA
  325. #define mythread_once(func) \
  326. do { \
  327. static INIT_ONCE once_ = INIT_ONCE_STATIC_INIT; \
  328. BOOL pending_; \
  329. if (!InitOnceBeginInitialize(&once_, 0, &pending_, NULL)) \
  330. abort(); \
  331. if (pending_) { \
  332. func(); \
  333. if (!InitOnceComplete(&once_, 0, NULL)) \
  334. abort(); \
  335. } \
  336. } while (0)
  337. #endif
  338. // mythread_sigmask() isn't available on Windows. Even a dummy version would
  339. // make no sense because the other POSIX signal functions are missing anyway.
  340. static inline int
  341. mythread_create(mythread *thread,
  342. unsigned int (__stdcall *func)(void *arg), void *arg)
  343. {
  344. uintptr_t ret = _beginthreadex(NULL, 0, func, arg, 0, NULL);
  345. if (ret == 0)
  346. return -1;
  347. *thread = (HANDLE)ret;
  348. return 0;
  349. }
  350. static inline int
  351. mythread_join(mythread thread)
  352. {
  353. int ret = 0;
  354. if (WaitForSingleObject(thread, INFINITE) != WAIT_OBJECT_0)
  355. ret = -1;
  356. if (!CloseHandle(thread))
  357. ret = -1;
  358. return ret;
  359. }
  360. static inline int
  361. mythread_mutex_init(mythread_mutex *mutex)
  362. {
  363. InitializeCriticalSection(mutex);
  364. return 0;
  365. }
  366. static inline void
  367. mythread_mutex_destroy(mythread_mutex *mutex)
  368. {
  369. DeleteCriticalSection(mutex);
  370. }
  371. static inline void
  372. mythread_mutex_lock(mythread_mutex *mutex)
  373. {
  374. EnterCriticalSection(mutex);
  375. }
  376. static inline void
  377. mythread_mutex_unlock(mythread_mutex *mutex)
  378. {
  379. LeaveCriticalSection(mutex);
  380. }
  381. static inline int
  382. mythread_cond_init(mythread_cond *cond)
  383. {
  384. #ifdef MYTHREAD_WIN95
  385. *cond = CreateEvent(NULL, FALSE, FALSE, NULL);
  386. return *cond == NULL ? -1 : 0;
  387. #else
  388. InitializeConditionVariable(cond);
  389. return 0;
  390. #endif
  391. }
  392. static inline void
  393. mythread_cond_destroy(mythread_cond *cond)
  394. {
  395. #ifdef MYTHREAD_WIN95
  396. CloseHandle(*cond);
  397. #else
  398. (void)cond;
  399. #endif
  400. }
  401. static inline void
  402. mythread_cond_signal(mythread_cond *cond)
  403. {
  404. #ifdef MYTHREAD_WIN95
  405. SetEvent(*cond);
  406. #else
  407. WakeConditionVariable(cond);
  408. #endif
  409. }
  410. static inline void
  411. mythread_cond_wait(mythread_cond *cond, mythread_mutex *mutex)
  412. {
  413. #ifdef MYTHREAD_WIN95
  414. LeaveCriticalSection(mutex);
  415. WaitForSingleObject(*cond, INFINITE);
  416. EnterCriticalSection(mutex);
  417. #else
  418. BOOL ret = SleepConditionVariableCS(cond, mutex, INFINITE);
  419. assert(ret);
  420. (void)ret;
  421. #endif
  422. }
  423. static inline int
  424. mythread_cond_timedwait(mythread_cond *cond, mythread_mutex *mutex,
  425. const mythread_condtime *condtime)
  426. {
  427. #ifdef MYTHREAD_WIN95
  428. LeaveCriticalSection(mutex);
  429. #endif
  430. DWORD elapsed = GetTickCount() - condtime->start;
  431. DWORD timeout = elapsed >= condtime->timeout
  432. ? 0 : condtime->timeout - elapsed;
  433. #ifdef MYTHREAD_WIN95
  434. DWORD ret = WaitForSingleObject(*cond, timeout);
  435. assert(ret == WAIT_OBJECT_0 || ret == WAIT_TIMEOUT);
  436. EnterCriticalSection(mutex);
  437. return ret == WAIT_TIMEOUT;
  438. #else
  439. BOOL ret = SleepConditionVariableCS(cond, mutex, timeout);
  440. assert(ret || GetLastError() == ERROR_TIMEOUT);
  441. return !ret;
  442. #endif
  443. }
  444. static inline void
  445. mythread_condtime_set(mythread_condtime *condtime, const mythread_cond *cond,
  446. uint32_t timeout)
  447. {
  448. (void)cond;
  449. condtime->start = GetTickCount();
  450. condtime->timeout = timeout;
  451. }
  452. #endif
  453. #endif