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