tinycthread.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595
  1. /* -*- mode: c; tab-width: 2; indent-tabs-mode: nil; -*-
  2. Copyright (c) 2012 Marcus Geelnard
  3. This software is provided 'as-is', without any express or implied
  4. warranty. In no event will the authors be held liable for any damages
  5. arising from the use of this software.
  6. Permission is granted to anyone to use this software for any purpose,
  7. including commercial applications, and to alter it and redistribute it
  8. freely, subject to the following restrictions:
  9. 1. The origin of this software must not be misrepresented; you must not
  10. claim that you wrote the original software. If you use this software
  11. in a product, an acknowledgment in the product documentation would be
  12. appreciated but is not required.
  13. 2. Altered source versions must be plainly marked as such, and must not be
  14. misrepresented as being the original software.
  15. 3. This notice may not be removed or altered from any source
  16. distribution.
  17. */
  18. /* 2013-01-06 Camilla Löwy <elmindreda@glfw.org>
  19. *
  20. * Added casts from time_t to DWORD to avoid warnings on VC++.
  21. * Fixed time retrieval on POSIX systems.
  22. */
  23. #include "tinycthread.h"
  24. #include <stdlib.h>
  25. /* Platform specific includes */
  26. #if defined(_TTHREAD_POSIX_)
  27. #include <signal.h>
  28. #include <sched.h>
  29. #include <unistd.h>
  30. #include <sys/time.h>
  31. #include <errno.h>
  32. #elif defined(_TTHREAD_WIN32_)
  33. #include <process.h>
  34. #include <sys/timeb.h>
  35. #endif
  36. /* Standard, good-to-have defines */
  37. #ifndef NULL
  38. #define NULL (void*)0
  39. #endif
  40. #ifndef TRUE
  41. #define TRUE 1
  42. #endif
  43. #ifndef FALSE
  44. #define FALSE 0
  45. #endif
  46. int mtx_init(mtx_t *mtx, int type)
  47. {
  48. #if defined(_TTHREAD_WIN32_)
  49. mtx->mAlreadyLocked = FALSE;
  50. mtx->mRecursive = type & mtx_recursive;
  51. InitializeCriticalSection(&mtx->mHandle);
  52. return thrd_success;
  53. #else
  54. int ret;
  55. pthread_mutexattr_t attr;
  56. pthread_mutexattr_init(&attr);
  57. if (type & mtx_recursive)
  58. {
  59. pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
  60. }
  61. ret = pthread_mutex_init(mtx, &attr);
  62. pthread_mutexattr_destroy(&attr);
  63. return ret == 0 ? thrd_success : thrd_error;
  64. #endif
  65. }
  66. void mtx_destroy(mtx_t *mtx)
  67. {
  68. #if defined(_TTHREAD_WIN32_)
  69. DeleteCriticalSection(&mtx->mHandle);
  70. #else
  71. pthread_mutex_destroy(mtx);
  72. #endif
  73. }
  74. int mtx_lock(mtx_t *mtx)
  75. {
  76. #if defined(_TTHREAD_WIN32_)
  77. EnterCriticalSection(&mtx->mHandle);
  78. if (!mtx->mRecursive)
  79. {
  80. while(mtx->mAlreadyLocked) Sleep(1000); /* Simulate deadlock... */
  81. mtx->mAlreadyLocked = TRUE;
  82. }
  83. return thrd_success;
  84. #else
  85. return pthread_mutex_lock(mtx) == 0 ? thrd_success : thrd_error;
  86. #endif
  87. }
  88. int mtx_timedlock(mtx_t *mtx, const struct timespec *ts)
  89. {
  90. /* FIXME! */
  91. (void)mtx;
  92. (void)ts;
  93. return thrd_error;
  94. }
  95. int mtx_trylock(mtx_t *mtx)
  96. {
  97. #if defined(_TTHREAD_WIN32_)
  98. int ret = TryEnterCriticalSection(&mtx->mHandle) ? thrd_success : thrd_busy;
  99. if ((!mtx->mRecursive) && (ret == thrd_success) && mtx->mAlreadyLocked)
  100. {
  101. LeaveCriticalSection(&mtx->mHandle);
  102. ret = thrd_busy;
  103. }
  104. return ret;
  105. #else
  106. return (pthread_mutex_trylock(mtx) == 0) ? thrd_success : thrd_busy;
  107. #endif
  108. }
  109. int mtx_unlock(mtx_t *mtx)
  110. {
  111. #if defined(_TTHREAD_WIN32_)
  112. mtx->mAlreadyLocked = FALSE;
  113. LeaveCriticalSection(&mtx->mHandle);
  114. return thrd_success;
  115. #else
  116. return pthread_mutex_unlock(mtx) == 0 ? thrd_success : thrd_error;;
  117. #endif
  118. }
  119. #if defined(_TTHREAD_WIN32_)
  120. #define _CONDITION_EVENT_ONE 0
  121. #define _CONDITION_EVENT_ALL 1
  122. #endif
  123. int cnd_init(cnd_t *cond)
  124. {
  125. #if defined(_TTHREAD_WIN32_)
  126. cond->mWaitersCount = 0;
  127. /* Init critical section */
  128. InitializeCriticalSection(&cond->mWaitersCountLock);
  129. /* Init events */
  130. cond->mEvents[_CONDITION_EVENT_ONE] = CreateEvent(NULL, FALSE, FALSE, NULL);
  131. if (cond->mEvents[_CONDITION_EVENT_ONE] == NULL)
  132. {
  133. cond->mEvents[_CONDITION_EVENT_ALL] = NULL;
  134. return thrd_error;
  135. }
  136. cond->mEvents[_CONDITION_EVENT_ALL] = CreateEvent(NULL, TRUE, FALSE, NULL);
  137. if (cond->mEvents[_CONDITION_EVENT_ALL] == NULL)
  138. {
  139. CloseHandle(cond->mEvents[_CONDITION_EVENT_ONE]);
  140. cond->mEvents[_CONDITION_EVENT_ONE] = NULL;
  141. return thrd_error;
  142. }
  143. return thrd_success;
  144. #else
  145. return pthread_cond_init(cond, NULL) == 0 ? thrd_success : thrd_error;
  146. #endif
  147. }
  148. void cnd_destroy(cnd_t *cond)
  149. {
  150. #if defined(_TTHREAD_WIN32_)
  151. if (cond->mEvents[_CONDITION_EVENT_ONE] != NULL)
  152. {
  153. CloseHandle(cond->mEvents[_CONDITION_EVENT_ONE]);
  154. }
  155. if (cond->mEvents[_CONDITION_EVENT_ALL] != NULL)
  156. {
  157. CloseHandle(cond->mEvents[_CONDITION_EVENT_ALL]);
  158. }
  159. DeleteCriticalSection(&cond->mWaitersCountLock);
  160. #else
  161. pthread_cond_destroy(cond);
  162. #endif
  163. }
  164. int cnd_signal(cnd_t *cond)
  165. {
  166. #if defined(_TTHREAD_WIN32_)
  167. int haveWaiters;
  168. /* Are there any waiters? */
  169. EnterCriticalSection(&cond->mWaitersCountLock);
  170. haveWaiters = (cond->mWaitersCount > 0);
  171. LeaveCriticalSection(&cond->mWaitersCountLock);
  172. /* If we have any waiting threads, send them a signal */
  173. if(haveWaiters)
  174. {
  175. if (SetEvent(cond->mEvents[_CONDITION_EVENT_ONE]) == 0)
  176. {
  177. return thrd_error;
  178. }
  179. }
  180. return thrd_success;
  181. #else
  182. return pthread_cond_signal(cond) == 0 ? thrd_success : thrd_error;
  183. #endif
  184. }
  185. int cnd_broadcast(cnd_t *cond)
  186. {
  187. #if defined(_TTHREAD_WIN32_)
  188. int haveWaiters;
  189. /* Are there any waiters? */
  190. EnterCriticalSection(&cond->mWaitersCountLock);
  191. haveWaiters = (cond->mWaitersCount > 0);
  192. LeaveCriticalSection(&cond->mWaitersCountLock);
  193. /* If we have any waiting threads, send them a signal */
  194. if(haveWaiters)
  195. {
  196. if (SetEvent(cond->mEvents[_CONDITION_EVENT_ALL]) == 0)
  197. {
  198. return thrd_error;
  199. }
  200. }
  201. return thrd_success;
  202. #else
  203. return pthread_cond_signal(cond) == 0 ? thrd_success : thrd_error;
  204. #endif
  205. }
  206. #if defined(_TTHREAD_WIN32_)
  207. static int _cnd_timedwait_win32(cnd_t *cond, mtx_t *mtx, DWORD timeout)
  208. {
  209. int result, lastWaiter;
  210. /* Increment number of waiters */
  211. EnterCriticalSection(&cond->mWaitersCountLock);
  212. ++ cond->mWaitersCount;
  213. LeaveCriticalSection(&cond->mWaitersCountLock);
  214. /* Release the mutex while waiting for the condition (will decrease
  215. the number of waiters when done)... */
  216. mtx_unlock(mtx);
  217. /* Wait for either event to become signaled due to cnd_signal() or
  218. cnd_broadcast() being called */
  219. result = WaitForMultipleObjects(2, cond->mEvents, FALSE, timeout);
  220. if (result == WAIT_TIMEOUT)
  221. {
  222. return thrd_timeout;
  223. }
  224. else if (result == (int)WAIT_FAILED)
  225. {
  226. return thrd_error;
  227. }
  228. /* Check if we are the last waiter */
  229. EnterCriticalSection(&cond->mWaitersCountLock);
  230. -- cond->mWaitersCount;
  231. lastWaiter = (result == (WAIT_OBJECT_0 + _CONDITION_EVENT_ALL)) &&
  232. (cond->mWaitersCount == 0);
  233. LeaveCriticalSection(&cond->mWaitersCountLock);
  234. /* If we are the last waiter to be notified to stop waiting, reset the event */
  235. if (lastWaiter)
  236. {
  237. if (ResetEvent(cond->mEvents[_CONDITION_EVENT_ALL]) == 0)
  238. {
  239. return thrd_error;
  240. }
  241. }
  242. /* Re-acquire the mutex */
  243. mtx_lock(mtx);
  244. return thrd_success;
  245. }
  246. #endif
  247. int cnd_wait(cnd_t *cond, mtx_t *mtx)
  248. {
  249. #if defined(_TTHREAD_WIN32_)
  250. return _cnd_timedwait_win32(cond, mtx, INFINITE);
  251. #else
  252. return pthread_cond_wait(cond, mtx) == 0 ? thrd_success : thrd_error;
  253. #endif
  254. }
  255. int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts)
  256. {
  257. #if defined(_TTHREAD_WIN32_)
  258. struct timespec now;
  259. if (clock_gettime(CLOCK_REALTIME, &now) == 0)
  260. {
  261. DWORD delta = (DWORD) ((ts->tv_sec - now.tv_sec) * 1000 +
  262. (ts->tv_nsec - now.tv_nsec + 500000) / 1000000);
  263. return _cnd_timedwait_win32(cond, mtx, delta);
  264. }
  265. else
  266. return thrd_error;
  267. #else
  268. int ret;
  269. ret = pthread_cond_timedwait(cond, mtx, ts);
  270. if (ret == ETIMEDOUT)
  271. {
  272. return thrd_timeout;
  273. }
  274. return ret == 0 ? thrd_success : thrd_error;
  275. #endif
  276. }
  277. /** Information to pass to the new thread (what to run). */
  278. typedef struct {
  279. thrd_start_t mFunction; /**< Pointer to the function to be executed. */
  280. void * mArg; /**< Function argument for the thread function. */
  281. } _thread_start_info;
  282. /* Thread wrapper function. */
  283. #if defined(_TTHREAD_WIN32_)
  284. static unsigned WINAPI _thrd_wrapper_function(void * aArg)
  285. #elif defined(_TTHREAD_POSIX_)
  286. static void * _thrd_wrapper_function(void * aArg)
  287. #endif
  288. {
  289. thrd_start_t fun;
  290. void *arg;
  291. int res;
  292. #if defined(_TTHREAD_POSIX_)
  293. void *pres;
  294. #endif
  295. /* Get thread startup information */
  296. _thread_start_info *ti = (_thread_start_info *) aArg;
  297. fun = ti->mFunction;
  298. arg = ti->mArg;
  299. /* The thread is responsible for freeing the startup information */
  300. free((void *)ti);
  301. /* Call the actual client thread function */
  302. res = fun(arg);
  303. #if defined(_TTHREAD_WIN32_)
  304. return res;
  305. #else
  306. pres = malloc(sizeof(int));
  307. if (pres != NULL)
  308. {
  309. *(int*)pres = res;
  310. }
  311. return pres;
  312. #endif
  313. }
  314. int thrd_create(thrd_t *thr, thrd_start_t func, void *arg)
  315. {
  316. /* Fill out the thread startup information (passed to the thread wrapper,
  317. which will eventually free it) */
  318. _thread_start_info* ti = (_thread_start_info*)malloc(sizeof(_thread_start_info));
  319. if (ti == NULL)
  320. {
  321. return thrd_nomem;
  322. }
  323. ti->mFunction = func;
  324. ti->mArg = arg;
  325. /* Create the thread */
  326. #if defined(_TTHREAD_WIN32_)
  327. *thr = (HANDLE)_beginthreadex(NULL, 0, _thrd_wrapper_function, (void *)ti, 0, NULL);
  328. #elif defined(_TTHREAD_POSIX_)
  329. if(pthread_create(thr, NULL, _thrd_wrapper_function, (void *)ti) != 0)
  330. {
  331. *thr = 0;
  332. }
  333. #endif
  334. /* Did we fail to create the thread? */
  335. if(!*thr)
  336. {
  337. free(ti);
  338. return thrd_error;
  339. }
  340. return thrd_success;
  341. }
  342. thrd_t thrd_current(void)
  343. {
  344. #if defined(_TTHREAD_WIN32_)
  345. return GetCurrentThread();
  346. #else
  347. return pthread_self();
  348. #endif
  349. }
  350. int thrd_detach(thrd_t thr)
  351. {
  352. /* FIXME! */
  353. (void)thr;
  354. return thrd_error;
  355. }
  356. int thrd_equal(thrd_t thr0, thrd_t thr1)
  357. {
  358. #if defined(_TTHREAD_WIN32_)
  359. return thr0 == thr1;
  360. #else
  361. return pthread_equal(thr0, thr1);
  362. #endif
  363. }
  364. void thrd_exit(int res)
  365. {
  366. #if defined(_TTHREAD_WIN32_)
  367. ExitThread(res);
  368. #else
  369. void *pres = malloc(sizeof(int));
  370. if (pres != NULL)
  371. {
  372. *(int*)pres = res;
  373. }
  374. pthread_exit(pres);
  375. #endif
  376. }
  377. int thrd_join(thrd_t thr, int *res)
  378. {
  379. #if defined(_TTHREAD_WIN32_)
  380. if (WaitForSingleObject(thr, INFINITE) == WAIT_FAILED)
  381. {
  382. return thrd_error;
  383. }
  384. if (res != NULL)
  385. {
  386. DWORD dwRes;
  387. GetExitCodeThread(thr, &dwRes);
  388. *res = dwRes;
  389. }
  390. #elif defined(_TTHREAD_POSIX_)
  391. void *pres;
  392. int ires = 0;
  393. if (pthread_join(thr, &pres) != 0)
  394. {
  395. return thrd_error;
  396. }
  397. if (pres != NULL)
  398. {
  399. ires = *(int*)pres;
  400. free(pres);
  401. }
  402. if (res != NULL)
  403. {
  404. *res = ires;
  405. }
  406. #endif
  407. return thrd_success;
  408. }
  409. int thrd_sleep(const struct timespec *time_point, struct timespec *remaining)
  410. {
  411. struct timespec now;
  412. #if defined(_TTHREAD_WIN32_)
  413. DWORD delta;
  414. #else
  415. long delta;
  416. #endif
  417. /* Get the current time */
  418. if (clock_gettime(CLOCK_REALTIME, &now) != 0)
  419. return -2; // FIXME: Some specific error code?
  420. #if defined(_TTHREAD_WIN32_)
  421. /* Delta in milliseconds */
  422. delta = (DWORD) ((time_point->tv_sec - now.tv_sec) * 1000 +
  423. (time_point->tv_nsec - now.tv_nsec + 500000) / 1000000);
  424. if (delta > 0)
  425. {
  426. Sleep(delta);
  427. }
  428. #else
  429. /* Delta in microseconds */
  430. delta = (time_point->tv_sec - now.tv_sec) * 1000000L +
  431. (time_point->tv_nsec - now.tv_nsec + 500L) / 1000L;
  432. /* On some systems, the usleep argument must be < 1000000 */
  433. while (delta > 999999L)
  434. {
  435. usleep(999999);
  436. delta -= 999999L;
  437. }
  438. if (delta > 0L)
  439. {
  440. usleep((useconds_t)delta);
  441. }
  442. #endif
  443. /* We don't support waking up prematurely (yet) */
  444. if (remaining)
  445. {
  446. remaining->tv_sec = 0;
  447. remaining->tv_nsec = 0;
  448. }
  449. return 0;
  450. }
  451. void thrd_yield(void)
  452. {
  453. #if defined(_TTHREAD_WIN32_)
  454. Sleep(0);
  455. #else
  456. sched_yield();
  457. #endif
  458. }
  459. int tss_create(tss_t *key, tss_dtor_t dtor)
  460. {
  461. #if defined(_TTHREAD_WIN32_)
  462. /* FIXME: The destructor function is not supported yet... */
  463. if (dtor != NULL)
  464. {
  465. return thrd_error;
  466. }
  467. *key = TlsAlloc();
  468. if (*key == TLS_OUT_OF_INDEXES)
  469. {
  470. return thrd_error;
  471. }
  472. #else
  473. if (pthread_key_create(key, dtor) != 0)
  474. {
  475. return thrd_error;
  476. }
  477. #endif
  478. return thrd_success;
  479. }
  480. void tss_delete(tss_t key)
  481. {
  482. #if defined(_TTHREAD_WIN32_)
  483. TlsFree(key);
  484. #else
  485. pthread_key_delete(key);
  486. #endif
  487. }
  488. void *tss_get(tss_t key)
  489. {
  490. #if defined(_TTHREAD_WIN32_)
  491. return TlsGetValue(key);
  492. #else
  493. return pthread_getspecific(key);
  494. #endif
  495. }
  496. int tss_set(tss_t key, void *val)
  497. {
  498. #if defined(_TTHREAD_WIN32_)
  499. if (TlsSetValue(key, val) == 0)
  500. {
  501. return thrd_error;
  502. }
  503. #else
  504. if (pthread_setspecific(key, val) != 0)
  505. {
  506. return thrd_error;
  507. }
  508. #endif
  509. return thrd_success;
  510. }
  511. #if defined(_TTHREAD_EMULATE_CLOCK_GETTIME_)
  512. int _tthread_clock_gettime(clockid_t clk_id, struct timespec *ts)
  513. {
  514. #if defined(_TTHREAD_WIN32_)
  515. struct _timeb tb;
  516. _ftime(&tb);
  517. ts->tv_sec = (time_t)tb.time;
  518. ts->tv_nsec = 1000000L * (long)tb.millitm;
  519. #else
  520. struct timeval tv;
  521. gettimeofday(&tv, NULL);
  522. ts->tv_sec = (time_t)tv.tv_sec;
  523. ts->tv_nsec = 1000L * (long)tv.tv_usec;
  524. #endif
  525. return 0;
  526. }
  527. #endif // _TTHREAD_EMULATE_CLOCK_GETTIME_