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(¤t_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