1 | /* -*- mode: c; tab-width: 2; indent-tabs-mode: nil; -*- |
2 | Copyright (c) 2012 Marcus Geelnard |
3 | Copyright (c) 2013-2014 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 "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 |
55 | extern "C" { |
56 | #endif |
57 | |
58 | static RD_TLS int thrd_is_detached; |
59 | |
60 | |
61 | int 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 | |
94 | void 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 | |
110 | int 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 | |
140 | int 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(¤t_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 | |
235 | int 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 | |
267 | int 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 | |
293 | int 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 | |
322 | void 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 | |
339 | int 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 | |
364 | int 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_) |
390 | int _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 | |
437 | int 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 | |
446 | int 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_) |
474 | struct TinyCThreadTSSData { |
475 | void* value; |
476 | tss_t key; |
477 | struct TinyCThreadTSSData* next; |
478 | }; |
479 | |
480 | static tss_dtor_t _tinycthread_tss_dtors[1088] = { NULL, }; |
481 | |
482 | static _Thread_local struct TinyCThreadTSSData* _tinycthread_tss_head = NULL; |
483 | static _Thread_local struct TinyCThreadTSSData* _tinycthread_tss_tail = NULL; |
484 | |
485 | static void _tinycthread_tss_cleanup (void); |
486 | |
487 | static 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 | |
521 | static 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). */ |
551 | typedef 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_) |
558 | static DWORD WINAPI _thrd_wrapper_function(LPVOID aArg) |
559 | #elif defined(_TTHREAD_POSIX_) |
560 | static 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 | |
590 | int 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 | |
626 | thrd_t thrd_current(void) |
627 | { |
628 | #if defined(_TTHREAD_WIN32_) |
629 | return GetCurrentThread(); |
630 | #else |
631 | return pthread_self(); |
632 | #endif |
633 | } |
634 | |
635 | int 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 | |
646 | int 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 | |
655 | void 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 | |
669 | int 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 | |
704 | int 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 | |
738 | void thrd_yield(void) |
739 | { |
740 | #if defined(_TTHREAD_WIN32_) |
741 | Sleep(0); |
742 | #else |
743 | sched_yield(); |
744 | #endif |
745 | } |
746 | |
747 | int 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 | |
765 | void 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 | |
802 | void *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 | |
816 | int 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_) |
863 | int _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_) |
893 | void 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 | |