1 /* -*- mode: c; tab-width: 2; indent-tabs-mode: nil; -*-
2 Copyright (c) 2012 Marcus Geelnard
3 Copyright (c) 2013-2016 Evan Nemerson
4 
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8 
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12 
13     1. The origin of this software must not be misrepresented; you must not
14     claim that you wrote the original software. If you use this software
15     in a product, an acknowledgment in the product documentation would be
16     appreciated but is not required.
17 
18     2. Altered source versions must be plainly marked as such, and must not be
19     misrepresented as being the original software.
20 
21     3. This notice may not be removed or altered from any source
22     distribution.
23 */
24 
25 #include "tinycthread.h"
26 #include "badthreads.h"
27 #include <stdlib.h>
28 
29 /* Platform specific includes */
30 #if defined(_TTHREAD_POSIX_)
31   #include <signal.h>
32   #include <sched.h>
33   #include <unistd.h>
34   #include <sys/time.h>
35   #include <errno.h>
36 #elif defined(_TTHREAD_WIN32_)
37   #include <process.h>
38   #include <sys/timeb.h>
39 #endif
40 
41 /* Standard, good-to-have defines */
42 #ifndef NULL
43   #define NULL (void*)0
44 #endif
45 #ifndef TRUE
46   #define TRUE 1
47 #endif
48 #ifndef FALSE
49   #define FALSE 0
50 #endif
51 
52 #ifdef __cplusplus
53 extern "C" {
54 #endif
55 
56 
tct_mtx_init(tct_mtx_t * mtx,int type)57 int tct_mtx_init(tct_mtx_t *mtx, int type)
58 {
59 #if defined(_TTHREAD_WIN32_)
60   mtx->mAlreadyLocked = FALSE;
61   mtx->mRecursive = type & tct_mtx_recursive;
62   mtx->mTimed = type & tct_mtx_timed;
63   if (!mtx->mTimed)
64   {
65     InitializeCriticalSection(&(mtx->mHandle.cs));
66   }
67   else
68   {
69     mtx->mHandle.mut = CreateMutex(NULL, FALSE, NULL);
70     if (mtx->mHandle.mut == NULL)
71     {
72       return tct_thrd_error;
73     }
74   }
75   return tct_thrd_success;
76 #else
77   int ret;
78   pthread_mutexattr_t attr;
79   pthread_mutexattr_init(&attr);
80   if (type & tct_mtx_recursive)
81   {
82     pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
83   }
84   ret = pthread_mutex_init(mtx, &attr);
85   pthread_mutexattr_destroy(&attr);
86   return ret == 0 ? tct_thrd_success : tct_thrd_error;
87 #endif
88 }
89 
tct_mtx_destroy(tct_mtx_t * mtx)90 void tct_mtx_destroy(tct_mtx_t *mtx)
91 {
92 #if defined(_TTHREAD_WIN32_)
93   if (!mtx->mTimed)
94   {
95     DeleteCriticalSection(&(mtx->mHandle.cs));
96   }
97   else
98   {
99     CloseHandle(mtx->mHandle.mut);
100   }
101 #else
102   pthread_mutex_destroy(mtx);
103 #endif
104 }
105 
tct_mtx_lock(tct_mtx_t * mtx)106 int tct_mtx_lock(tct_mtx_t *mtx)
107 {
108 #if defined(_TTHREAD_WIN32_)
109   if (!mtx->mTimed)
110   {
111     EnterCriticalSection(&(mtx->mHandle.cs));
112   }
113   else
114   {
115     switch (WaitForSingleObject(mtx->mHandle.mut, INFINITE))
116     {
117       case WAIT_OBJECT_0:
118         break;
119       case WAIT_ABANDONED:
120       default:
121         return tct_thrd_error;
122     }
123   }
124 
125   if (!mtx->mRecursive)
126   {
127     while(mtx->mAlreadyLocked) Sleep(1); /* Simulate deadlock... */
128     mtx->mAlreadyLocked = TRUE;
129   }
130   return tct_thrd_success;
131 #else
132   return pthread_mutex_lock(mtx) == 0 ? tct_thrd_success : tct_thrd_error;
133 #endif
134 }
135 
tct_mtx_timedlock(tct_mtx_t * mtx,const struct timespec * ts)136 int tct_mtx_timedlock(tct_mtx_t *mtx, const struct timespec *ts)
137 {
138 #if defined(_TTHREAD_WIN32_)
139   struct timespec current_ts;
140   DWORD timeoutMs;
141 
142   if (!mtx->mTimed)
143   {
144     return tct_thrd_error;
145   }
146 
147   timespec_get(&current_ts, TIME_UTC);
148 
149   if ((current_ts.tv_sec > ts->tv_sec) || ((current_ts.tv_sec == ts->tv_sec) && (current_ts.tv_nsec >= ts->tv_nsec)))
150   {
151     timeoutMs = 0;
152   }
153   else
154   {
155     timeoutMs  = (DWORD)(ts->tv_sec  - current_ts.tv_sec)  * 1000;
156     timeoutMs += (ts->tv_nsec - current_ts.tv_nsec) / 1000000;
157     timeoutMs += 1;
158   }
159 
160   /* TODO: the timeout for WaitForSingleObject doesn't include time
161      while the computer is asleep. */
162   switch (WaitForSingleObject(mtx->mHandle.mut, timeoutMs))
163   {
164     case WAIT_OBJECT_0:
165       break;
166     case WAIT_TIMEOUT:
167       return tct_thrd_timedout;
168     case WAIT_ABANDONED:
169     default:
170       return tct_thrd_error;
171   }
172 
173   if (!mtx->mRecursive)
174   {
175     while(mtx->mAlreadyLocked) Sleep(1); /* Simulate deadlock... */
176     mtx->mAlreadyLocked = TRUE;
177   }
178 
179   return tct_thrd_success;
180 #elif defined(_POSIX_TIMEOUTS) && (_POSIX_TIMEOUTS >= 200112L) && defined(_POSIX_THREADS) && (_POSIX_THREADS >= 200112L)
181   switch (pthread_mutex_timedlock(mtx, ts)) {
182     case 0:
183       return tct_thrd_success;
184     case ETIMEDOUT:
185       return tct_thrd_timedout;
186     default:
187       return tct_thrd_error;
188   }
189 #else
190   int rc;
191   struct timespec cur, dur;
192 
193   /* Try to acquire the lock and, if we fail, sleep for 5ms. */
194   while ((rc = pthread_mutex_trylock (mtx)) == EBUSY) {
195     timespec_get(&cur, TIME_UTC);
196 
197     if ((cur.tv_sec > ts->tv_sec) || ((cur.tv_sec == ts->tv_sec) && (cur.tv_nsec >= ts->tv_nsec)))
198     {
199       break;
200     }
201 
202     dur.tv_sec = ts->tv_sec - cur.tv_sec;
203     dur.tv_nsec = ts->tv_nsec - cur.tv_nsec;
204     if (dur.tv_nsec < 0)
205     {
206       dur.tv_sec--;
207       dur.tv_nsec += 1000000000;
208     }
209 
210     if ((dur.tv_sec != 0) || (dur.tv_nsec > 5000000))
211     {
212       dur.tv_sec = 0;
213       dur.tv_nsec = 5000000;
214     }
215 
216     nanosleep(&dur, NULL);
217   }
218 
219   switch (rc) {
220     case 0:
221       return tct_thrd_success;
222     case ETIMEDOUT:
223     case EBUSY:
224       return tct_thrd_timedout;
225     default:
226       return tct_thrd_error;
227   }
228 #endif
229 }
230 
tct_mtx_trylock(tct_mtx_t * mtx)231 int tct_mtx_trylock(tct_mtx_t *mtx)
232 {
233 #if defined(_TTHREAD_WIN32_)
234   int ret;
235 
236   if (!mtx->mTimed)
237   {
238     ret = TryEnterCriticalSection(&(mtx->mHandle.cs)) ? tct_thrd_success : tct_thrd_busy;
239   }
240   else
241   {
242     ret = (WaitForSingleObject(mtx->mHandle.mut, 0) == WAIT_OBJECT_0) ? tct_thrd_success : tct_thrd_busy;
243   }
244 
245   if ((!mtx->mRecursive) && (ret == tct_thrd_success))
246   {
247     if (mtx->mAlreadyLocked)
248     {
249       LeaveCriticalSection(&(mtx->mHandle.cs));
250       ret = tct_thrd_busy;
251     }
252     else
253     {
254       mtx->mAlreadyLocked = TRUE;
255     }
256   }
257   return ret;
258 #else
259   return (pthread_mutex_trylock(mtx) == 0) ? tct_thrd_success : tct_thrd_busy;
260 #endif
261 }
262 
tct_mtx_unlock(tct_mtx_t * mtx)263 int tct_mtx_unlock(tct_mtx_t *mtx)
264 {
265 #if defined(_TTHREAD_WIN32_)
266   mtx->mAlreadyLocked = FALSE;
267   if (!mtx->mTimed)
268   {
269     LeaveCriticalSection(&(mtx->mHandle.cs));
270   }
271   else
272   {
273     if (!ReleaseMutex(mtx->mHandle.mut))
274     {
275       return tct_thrd_error;
276     }
277   }
278   return tct_thrd_success;
279 #else
280   return pthread_mutex_unlock(mtx) == 0 ? tct_thrd_success : tct_thrd_error;;
281 #endif
282 }
283 
284 #if defined(_TTHREAD_WIN32_)
285 #define _CONDITION_EVENT_ONE 0
286 #define _CONDITION_EVENT_ALL 1
287 #endif
288 
tct_cnd_init(tct_cnd_t * cond)289 int tct_cnd_init(tct_cnd_t *cond)
290 {
291 #if defined(_TTHREAD_WIN32_)
292   cond->mWaitersCount = 0;
293 
294   /* Init critical section */
295   InitializeCriticalSection(&cond->mWaitersCountLock);
296 
297   /* Init events */
298   cond->mEvents[_CONDITION_EVENT_ONE] = CreateEvent(NULL, FALSE, FALSE, NULL);
299   if (cond->mEvents[_CONDITION_EVENT_ONE] == NULL)
300   {
301     cond->mEvents[_CONDITION_EVENT_ALL] = NULL;
302     return tct_thrd_error;
303   }
304   cond->mEvents[_CONDITION_EVENT_ALL] = CreateEvent(NULL, TRUE, FALSE, NULL);
305   if (cond->mEvents[_CONDITION_EVENT_ALL] == NULL)
306   {
307     CloseHandle(cond->mEvents[_CONDITION_EVENT_ONE]);
308     cond->mEvents[_CONDITION_EVENT_ONE] = NULL;
309     return tct_thrd_error;
310   }
311 
312   return tct_thrd_success;
313 #else
314   return pthread_cond_init(cond, NULL) == 0 ? tct_thrd_success : tct_thrd_error;
315 #endif
316 }
317 
tct_cnd_destroy(tct_cnd_t * cond)318 void tct_cnd_destroy(tct_cnd_t *cond)
319 {
320 #if defined(_TTHREAD_WIN32_)
321   if (cond->mEvents[_CONDITION_EVENT_ONE] != NULL)
322   {
323     CloseHandle(cond->mEvents[_CONDITION_EVENT_ONE]);
324   }
325   if (cond->mEvents[_CONDITION_EVENT_ALL] != NULL)
326   {
327     CloseHandle(cond->mEvents[_CONDITION_EVENT_ALL]);
328   }
329   DeleteCriticalSection(&cond->mWaitersCountLock);
330 #else
331   pthread_cond_destroy(cond);
332 #endif
333 }
334 
tct_cnd_signal(tct_cnd_t * cond)335 int tct_cnd_signal(tct_cnd_t *cond)
336 {
337 #if defined(_TTHREAD_WIN32_)
338   int haveWaiters;
339 
340   /* Are there any waiters? */
341   EnterCriticalSection(&cond->mWaitersCountLock);
342   haveWaiters = (cond->mWaitersCount > 0);
343   LeaveCriticalSection(&cond->mWaitersCountLock);
344 
345   /* If we have any waiting threads, send them a signal */
346   if(haveWaiters)
347   {
348     if (SetEvent(cond->mEvents[_CONDITION_EVENT_ONE]) == 0)
349     {
350       return tct_thrd_error;
351     }
352   }
353 
354   return tct_thrd_success;
355 #else
356   return pthread_cond_signal(cond) == 0 ? tct_thrd_success : tct_thrd_error;
357 #endif
358 }
359 
tct_cnd_broadcast(tct_cnd_t * cond)360 int tct_cnd_broadcast(tct_cnd_t *cond)
361 {
362 #if defined(_TTHREAD_WIN32_)
363   int haveWaiters;
364 
365   /* Are there any waiters? */
366   EnterCriticalSection(&cond->mWaitersCountLock);
367   haveWaiters = (cond->mWaitersCount > 0);
368   LeaveCriticalSection(&cond->mWaitersCountLock);
369 
370   /* If we have any waiting threads, send them a signal */
371   if(haveWaiters)
372   {
373     if (SetEvent(cond->mEvents[_CONDITION_EVENT_ALL]) == 0)
374     {
375       return tct_thrd_error;
376     }
377   }
378 
379   return tct_thrd_success;
380 #else
381   return pthread_cond_broadcast(cond) == 0 ? tct_thrd_success : tct_thrd_error;
382 #endif
383 }
384 
385 #if defined(_TTHREAD_WIN32_)
_cnd_timedwait_win32(tct_cnd_t * cond,tct_mtx_t * mtx,DWORD timeout)386 static int _cnd_timedwait_win32(tct_cnd_t *cond, tct_mtx_t *mtx, DWORD timeout)
387 {
388   DWORD result;
389   int lastWaiter;
390 
391   /* Increment number of waiters */
392   EnterCriticalSection(&cond->mWaitersCountLock);
393   ++ cond->mWaitersCount;
394   LeaveCriticalSection(&cond->mWaitersCountLock);
395 
396   /* Release the mutex while waiting for the condition (will decrease
397      the number of waiters when done)... */
398   tct_mtx_unlock(mtx);
399 
400   /* Wait for either event to become signaled due to cnd_signal() or
401      cnd_broadcast() being called */
402   result = WaitForMultipleObjects(2, cond->mEvents, FALSE, timeout);
403   if (result == WAIT_TIMEOUT)
404   {
405     /* The mutex is locked again before the function returns, even if an error occurred */
406     tct_mtx_lock(mtx);
407     return tct_thrd_timedout;
408   }
409   else if (result == WAIT_FAILED)
410   {
411     /* The mutex is locked again before the function returns, even if an error occurred */
412     tct_mtx_lock(mtx);
413     return tct_thrd_error;
414   }
415 
416   /* Check if we are the last waiter */
417   EnterCriticalSection(&cond->mWaitersCountLock);
418   -- cond->mWaitersCount;
419   lastWaiter = (result == (WAIT_OBJECT_0 + _CONDITION_EVENT_ALL)) &&
420                (cond->mWaitersCount == 0);
421   LeaveCriticalSection(&cond->mWaitersCountLock);
422 
423   /* If we are the last waiter to be notified to stop waiting, reset the event */
424   if (lastWaiter)
425   {
426     if (ResetEvent(cond->mEvents[_CONDITION_EVENT_ALL]) == 0)
427     {
428       /* The mutex is locked again before the function returns, even if an error occurred */
429       tct_mtx_lock(mtx);
430       return tct_thrd_error;
431     }
432   }
433 
434   /* Re-acquire the mutex */
435   tct_mtx_lock(mtx);
436 
437   return tct_thrd_success;
438 }
439 #endif
440 
tct_cnd_wait(tct_cnd_t * cond,tct_mtx_t * mtx)441 int tct_cnd_wait(tct_cnd_t *cond, tct_mtx_t *mtx)
442 {
443 #if defined(_TTHREAD_WIN32_)
444   return _cnd_timedwait_win32(cond, mtx, INFINITE);
445 #else
446   return pthread_cond_wait(cond, mtx) == 0 ? tct_thrd_success : tct_thrd_error;
447 #endif
448 }
449 
tct_cnd_timedwait(tct_cnd_t * cond,tct_mtx_t * mtx,const struct timespec * ts)450 int tct_cnd_timedwait(tct_cnd_t *cond, tct_mtx_t *mtx, const struct timespec *ts)
451 {
452 #if defined(_TTHREAD_WIN32_)
453   struct timespec now;
454   if (timespec_get(&now, TIME_UTC) == TIME_UTC)
455   {
456     unsigned long long nowInMilliseconds = now.tv_sec * 1000 + now.tv_nsec / 1000000;
457     unsigned long long tsInMilliseconds  = ts->tv_sec * 1000 + ts->tv_nsec / 1000000;
458     DWORD delta = (tsInMilliseconds > nowInMilliseconds) ?
459       (DWORD)(tsInMilliseconds - nowInMilliseconds) : 0;
460     return _cnd_timedwait_win32(cond, mtx, delta);
461   }
462   else
463     return tct_thrd_error;
464 #else
465   int ret;
466   ret = pthread_cond_timedwait(cond, mtx, ts);
467   if (ret == ETIMEDOUT)
468   {
469     return tct_thrd_timedout;
470   }
471   return ret == 0 ? tct_thrd_success : tct_thrd_error;
472 #endif
473 }
474 
475 #if defined(_TTHREAD_WIN32_)
476 struct TinyCThreadTSSData {
477   void* value;
478   tct_tss_t key;
479   struct TinyCThreadTSSData* next;
480 };
481 
482 static tct_tss_dtor_t _tinycthread_tss_dtors[1088] = { NULL, };
483 
484 static _Thread_local struct TinyCThreadTSSData* _tinycthread_tss_head = NULL;
485 static _Thread_local struct TinyCThreadTSSData* _tinycthread_tss_tail = NULL;
486 
487 static void _tinycthread_tss_cleanup (void);
488 
_tinycthread_tss_cleanup(void)489 static void _tinycthread_tss_cleanup (void) {
490   struct TinyCThreadTSSData* data;
491   int iteration;
492   unsigned int again = 1;
493   void* value;
494 
495   for (iteration = 0 ; iteration < TCT_TSS_DTOR_ITERATIONS && again > 0 ; iteration++)
496   {
497     again = 0;
498     for (data = _tinycthread_tss_head ; data != NULL ; data = data->next)
499     {
500       if (data->value != NULL)
501       {
502         value = data->value;
503         data->value = NULL;
504 
505         if (_tinycthread_tss_dtors[data->key] != NULL)
506         {
507           again = 1;
508           _tinycthread_tss_dtors[data->key](value);
509         }
510       }
511     }
512   }
513 
514   while (_tinycthread_tss_head != NULL) {
515     data = _tinycthread_tss_head->next;
516     free (_tinycthread_tss_head);
517     _tinycthread_tss_head = data;
518   }
519   _tinycthread_tss_head = NULL;
520   _tinycthread_tss_tail = NULL;
521 }
522 
_tinycthread_tss_callback(PVOID h,DWORD dwReason,PVOID pv)523 static void NTAPI _tinycthread_tss_callback(PVOID h, DWORD dwReason, PVOID pv)
524 {
525   (void)h;
526   (void)pv;
527 
528   if (_tinycthread_tss_head != NULL && (dwReason == DLL_THREAD_DETACH || dwReason == DLL_PROCESS_DETACH))
529   {
530     _tinycthread_tss_cleanup();
531   }
532 }
533 
534 #if defined(_MSC_VER)
535   #ifdef _M_X64
536     #pragma const_seg(".CRT$XLB")
537   #else
538     #pragma data_seg(".CRT$XLB")
539   #endif
540   PIMAGE_TLS_CALLBACK p_thread_callback = _tinycthread_tss_callback;
541   #ifdef _M_X64
542     #pragma data_seg()
543   #else
544     #pragma const_seg()
545   #endif
546 #else
547   PIMAGE_TLS_CALLBACK p_thread_callback __attribute__((section(".CRT$XLB"))) = _tinycthread_tss_callback;
548 #endif
549 
550 #endif /* defined(_TTHREAD_WIN32_) */
551 
552 /** Information to pass to the new thread (what to run). */
553 typedef struct {
554   tct_thrd_start_t mFunction; /**< Pointer to the function to be executed. */
555   void * mArg;            /**< Function argument for the thread function. */
556 } _thread_start_info;
557 
558 /* Thread wrapper function. */
559 #if defined(_TTHREAD_WIN32_)
_thrd_wrapper_function(LPVOID aArg)560 static DWORD WINAPI _thrd_wrapper_function(LPVOID aArg)
561 #elif defined(_TTHREAD_POSIX_)
562 static void * _thrd_wrapper_function(void * aArg)
563 #endif
564 {
565   tct_thrd_start_t fun;
566   void *arg;
567   int  res;
568 
569   /* Get thread startup information */
570   _thread_start_info *ti = (_thread_start_info *) aArg;
571   fun = ti->mFunction;
572   arg = ti->mArg;
573 
574   /* The thread is responsible for freeing the startup information */
575   free((void *)ti);
576 
577   /* Call the actual client thread function */
578   res = fun(arg);
579 
580 #if defined(_TTHREAD_WIN32_)
581   if (_tinycthread_tss_head != NULL)
582   {
583     _tinycthread_tss_cleanup();
584   }
585 
586   return (DWORD)res;
587 #else
588   return (void*)(intptr_t)res;
589 #endif
590 }
591 
tct_thrd_create(tct_thrd_t * thr,tct_thrd_start_t func,void * arg)592 int tct_thrd_create(tct_thrd_t *thr, tct_thrd_start_t func, void *arg)
593 {
594   /* Fill out the thread startup information (passed to the thread wrapper,
595      which will eventually free it) */
596   _thread_start_info* ti = (_thread_start_info*)malloc(sizeof(_thread_start_info));
597   if (ti == NULL)
598   {
599     return tct_thrd_nomem;
600   }
601   ti->mFunction = func;
602   ti->mArg = arg;
603 
604   /* Create the thread */
605 #if defined(_TTHREAD_WIN32_)
606   *thr = CreateThread(NULL, 0, _thrd_wrapper_function, (LPVOID) ti, 0, NULL);
607 #elif defined(_TTHREAD_POSIX_)
608   if(pthread_create(thr, NULL, _thrd_wrapper_function, (void *)ti) != 0)
609   {
610     *thr = 0;
611   }
612 #endif
613 
614   /* Did we fail to create the thread? */
615   if(!*thr)
616   {
617     free(ti);
618     return tct_thrd_error;
619   }
620 
621   return tct_thrd_success;
622 }
623 
tct_thrd_current(void)624 tct_thrd_t tct_thrd_current(void)
625 {
626 #if defined(_TTHREAD_WIN32_)
627   return GetCurrentThread();
628 #else
629   return pthread_self();
630 #endif
631 }
632 
tct_thrd_detach(tct_thrd_t thr)633 int tct_thrd_detach(tct_thrd_t thr)
634 {
635 #if defined(_TTHREAD_WIN32_)
636   /* https://stackoverflow.com/questions/12744324/how-to-detach-a-thread-on-windows-c#answer-12746081 */
637   return CloseHandle(thr) != 0 ? tct_thrd_success : tct_thrd_error;
638 #else
639   return pthread_detach(thr) == 0 ? tct_thrd_success : tct_thrd_error;
640 #endif
641 }
642 
tct_thrd_equal(tct_thrd_t thr0,tct_thrd_t thr1)643 int tct_thrd_equal(tct_thrd_t thr0, tct_thrd_t thr1)
644 {
645 #if defined(_TTHREAD_WIN32_)
646   return GetThreadId(thr0) == GetThreadId(thr1);
647 #else
648   return pthread_equal(thr0, thr1);
649 #endif
650 }
651 
tct_thrd_exit(int res)652 void tct_thrd_exit(int res)
653 {
654 #if defined(_TTHREAD_WIN32_)
655   if (_tinycthread_tss_head != NULL)
656   {
657     _tinycthread_tss_cleanup();
658   }
659 
660   ExitThread((DWORD)res);
661 #else
662   pthread_exit((void*)(intptr_t)res);
663 #endif
664 }
665 
tct_thrd_join(tct_thrd_t thr,int * res)666 int tct_thrd_join(tct_thrd_t thr, int *res)
667 {
668 #if defined(_TTHREAD_WIN32_)
669   DWORD dwRes;
670 
671   if (WaitForSingleObject(thr, INFINITE) == WAIT_FAILED)
672   {
673     return tct_thrd_error;
674   }
675   if (res != NULL)
676   {
677     if (GetExitCodeThread(thr, &dwRes) != 0)
678     {
679       *res = (int) dwRes;
680     }
681     else
682     {
683       return tct_thrd_error;
684     }
685   }
686   CloseHandle(thr);
687 #elif defined(_TTHREAD_POSIX_)
688   void *pres;
689   if (pthread_join(thr, &pres) != 0)
690   {
691     return tct_thrd_error;
692   }
693   if (res != NULL)
694   {
695     *res = (int)(intptr_t)pres;
696   }
697 #endif
698   return tct_thrd_success;
699 }
700 
tct_thrd_sleep(const struct timespec * duration,struct timespec * remaining)701 int tct_thrd_sleep(const struct timespec *duration, struct timespec *remaining)
702 {
703 #if !defined(_TTHREAD_WIN32_)
704   int res = nanosleep(duration, remaining);
705   if (res == 0) {
706     return 0;
707   } else if (errno == EINTR) {
708     return -1;
709   } else {
710     return -2;
711   }
712 #else
713   struct timespec start;
714   DWORD t;
715 
716   timespec_get(&start, TIME_UTC);
717 
718   t = SleepEx((DWORD)(duration->tv_sec * 1000 +
719               duration->tv_nsec / 1000000 +
720               (((duration->tv_nsec % 1000000) == 0) ? 0 : 1)),
721               TRUE);
722 
723   if (t == 0) {
724     return 0;
725   } else {
726     if (remaining != NULL) {
727       timespec_get(remaining, TIME_UTC);
728       remaining->tv_sec -= start.tv_sec;
729       remaining->tv_nsec -= start.tv_nsec;
730       if (remaining->tv_nsec < 0)
731       {
732         remaining->tv_nsec += 1000000000;
733         remaining->tv_sec -= 1;
734       }
735     }
736 
737     return (t == WAIT_IO_COMPLETION) ? -1 : -2;
738   }
739 #endif
740 }
741 
tct_thrd_yield(void)742 void tct_thrd_yield(void)
743 {
744 #if defined(_TTHREAD_WIN32_)
745   Sleep(0);
746 #else
747   sched_yield();
748 #endif
749 }
750 
tct_tss_create(tct_tss_t * key,tct_tss_dtor_t dtor)751 int tct_tss_create(tct_tss_t *key, tct_tss_dtor_t dtor)
752 {
753 #if defined(_TTHREAD_WIN32_)
754   *key = TlsAlloc();
755   if (*key == TLS_OUT_OF_INDEXES)
756   {
757     return tct_thrd_error;
758   }
759   _tinycthread_tss_dtors[*key] = dtor;
760 #else
761   if (pthread_key_create(key, dtor) != 0)
762   {
763     return tct_thrd_error;
764   }
765 #endif
766   return tct_thrd_success;
767 }
768 
tct_tss_delete(tct_tss_t key)769 void tct_tss_delete(tct_tss_t key)
770 {
771 #if defined(_TTHREAD_WIN32_)
772   struct TinyCThreadTSSData* data = (struct TinyCThreadTSSData*) TlsGetValue (key);
773   struct TinyCThreadTSSData* prev = NULL;
774   if (data != NULL)
775   {
776     if (data == _tinycthread_tss_head)
777     {
778       _tinycthread_tss_head = data->next;
779     }
780     else
781     {
782       prev = _tinycthread_tss_head;
783       if (prev != NULL)
784       {
785         while (prev->next != data)
786         {
787           prev = prev->next;
788         }
789       }
790     }
791 
792     if (data == _tinycthread_tss_tail)
793     {
794       _tinycthread_tss_tail = prev;
795     }
796 
797     free (data);
798   }
799   _tinycthread_tss_dtors[key] = NULL;
800   TlsFree(key);
801 #else
802   pthread_key_delete(key);
803 #endif
804 }
805 
tct_tss_get(tct_tss_t key)806 void *tct_tss_get(tct_tss_t key)
807 {
808 #if defined(_TTHREAD_WIN32_)
809   struct TinyCThreadTSSData* data = (struct TinyCThreadTSSData*)TlsGetValue(key);
810   if (data == NULL)
811   {
812     return NULL;
813   }
814   return data->value;
815 #else
816   return pthread_getspecific(key);
817 #endif
818 }
819 
tct_tss_set(tct_tss_t key,void * val)820 int tct_tss_set(tct_tss_t key, void *val)
821 {
822 #if defined(_TTHREAD_WIN32_)
823   struct TinyCThreadTSSData* data = (struct TinyCThreadTSSData*)TlsGetValue(key);
824   if (data == NULL)
825   {
826     data = (struct TinyCThreadTSSData*)malloc(sizeof(struct TinyCThreadTSSData));
827     if (data == NULL)
828     {
829       return tct_thrd_error;
830 	}
831 
832     data->value = NULL;
833     data->key = key;
834     data->next = NULL;
835 
836     if (_tinycthread_tss_tail != NULL)
837     {
838       _tinycthread_tss_tail->next = data;
839     }
840     else
841     {
842       _tinycthread_tss_tail = data;
843     }
844 
845     if (_tinycthread_tss_head == NULL)
846     {
847       _tinycthread_tss_head = data;
848     }
849 
850     if (!TlsSetValue(key, data))
851     {
852       free (data);
853 	  return tct_thrd_error;
854     }
855   }
856   data->value = val;
857 #else
858   if (pthread_setspecific(key, val) != 0)
859   {
860     return tct_thrd_error;
861   }
862 #endif
863   return tct_thrd_success;
864 }
865 
866 #if defined(_TTHREAD_EMULATE_TIMESPEC_GET_)
_tthread_timespec_get(struct timespec * ts,int base)867 int _tthread_timespec_get(struct timespec *ts, int base)
868 {
869 #if defined(_TTHREAD_WIN32_)
870   struct _timeb tb;
871 #elif !defined(CLOCK_REALTIME)
872   struct timeval tv;
873 #endif
874 
875   if (base != TIME_UTC)
876   {
877     return 0;
878   }
879 
880 #if defined(_TTHREAD_WIN32_)
881   _ftime_s(&tb);
882   ts->tv_sec = (time_t)tb.time;
883   ts->tv_nsec = 1000000L * (long)tb.millitm;
884 #elif defined(CLOCK_REALTIME)
885   base = (clock_gettime(CLOCK_REALTIME, ts) == 0) ? base : 0;
886 #else
887   gettimeofday(&tv, NULL);
888   ts->tv_sec = (time_t)tv.tv_sec;
889   ts->tv_nsec = 1000L * (long)tv.tv_usec;
890 #endif
891 
892   return base;
893 }
894 #endif /* _TTHREAD_EMULATE_TIMESPEC_GET_ */
895 
896 #if defined(_TTHREAD_WIN32_)
tct_call_once(once_flag * flag,void (* func)(void))897 void tct_call_once(once_flag *flag, void (*func)(void))
898 {
899   /* The idea here is that we use a spin lock (via the
900      InterlockedCompareExchange function) to restrict access to the
901      critical section until we have initialized it, then we use the
902      critical section to block until the callback has completed
903      execution. */
904   while (flag->status < 3)
905   {
906     switch (flag->status)
907     {
908       case 0:
909         if (InterlockedCompareExchange (&(flag->status), 1, 0) == 0) {
910           InitializeCriticalSection(&(flag->lock));
911           EnterCriticalSection(&(flag->lock));
912           flag->status = 2;
913           func();
914           flag->status = 3;
915           LeaveCriticalSection(&(flag->lock));
916           return;
917         }
918         break;
919       case 1:
920         break;
921       case 2:
922         EnterCriticalSection(&(flag->lock));
923         LeaveCriticalSection(&(flag->lock));
924         break;
925     }
926   }
927 }
928 #endif /* defined(_TTHREAD_WIN32_) */
929 
930 #ifdef __cplusplus
931 }
932 #endif
933