commit 1327bb8
uint
·
2026-03-09 17:06:24 +0000 UTC
parent f68374b
migrate pthreads->tinycthread
7 files changed,
+1457,
-24
M
Makefile
+2,
-3
1@@ -3,8 +3,8 @@ CC ?= cc
2 VER = 2.26
3 GIT_VER != git describe --always --tags 2>/dev/null || echo unknown
4 CPPFLAGS = -D_POSIX_C_SOURCE=200809L -DGIT_VER=\"$(GIT_VER)\" -DVERSION=\"$(VER)\"
5-CFLAGS = -std=c99 -Wall -Wextra -Iserver/include -pthread
6-SRC = server/*.c
7+CFLAGS = -std=c99 -Wall -Wextra -Iserver/include -Iexternal -pthread
8+SRC = server/*.c external/tinycthread.c
9 OUT = parados
10
11 # Install Paths
12@@ -60,4 +60,3 @@ compile_flags:
13 for f in ${CPPFLAGS} ${CFLAGS}; do echo $$f >> compile_flags.txt; done
14
15 .PHONY: all release debug clean install install-conf uninstall compile_flags
16-
+22,
-0
1@@ -0,0 +1,22 @@
2+Copyright (c) 2012 Marcus Geelnard
3+ 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+
+931,
-0
1@@ -0,0 +1,931 @@
2+/* -*- mode: c; tab-width: 2; indent-tabs-mode: nil; -*-
3+Copyright (c) 2012 Marcus Geelnard
4+Copyright (c) 2013-2016 Evan Nemerson
5+
6+This software is provided 'as-is', without any express or implied
7+warranty. In no event will the authors be held liable for any damages
8+arising from the use of this software.
9+
10+Permission is granted to anyone to use this software for any purpose,
11+including commercial applications, and to alter it and redistribute it
12+freely, subject to the following restrictions:
13+
14+ 1. The origin of this software must not be misrepresented; you must not
15+ claim that you wrote the original software. If you use this software
16+ in a product, an acknowledgment in the product documentation would be
17+ appreciated but is not required.
18+
19+ 2. Altered source versions must be plainly marked as such, and must not be
20+ misrepresented as being the original software.
21+
22+ 3. This notice may not be removed or altered from any source
23+ distribution.
24+*/
25+
26+#include "tinycthread.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+
57+int mtx_init(mtx_t *mtx, int type)
58+{
59+#if defined(_TTHREAD_WIN32_)
60+ mtx->mAlreadyLocked = FALSE;
61+ mtx->mRecursive = type & mtx_recursive;
62+ mtx->mTimed = type & 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 thrd_error;
73+ }
74+ }
75+ return thrd_success;
76+#else
77+ int ret;
78+ pthread_mutexattr_t attr;
79+ pthread_mutexattr_init(&attr);
80+ if (type & 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 ? thrd_success : thrd_error;
87+#endif
88+}
89+
90+void mtx_destroy(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+
106+int mtx_lock(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 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 thrd_success;
131+#else
132+ return pthread_mutex_lock(mtx) == 0 ? thrd_success : thrd_error;
133+#endif
134+}
135+
136+int mtx_timedlock(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 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 thrd_timedout;
168+ case WAIT_ABANDONED:
169+ default:
170+ return 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 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 thrd_success;
184+ case ETIMEDOUT:
185+ return thrd_timedout;
186+ default:
187+ return 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 thrd_success;
222+ case ETIMEDOUT:
223+ case EBUSY:
224+ return thrd_timedout;
225+ default:
226+ return thrd_error;
227+ }
228+#endif
229+}
230+
231+int mtx_trylock(mtx_t *mtx)
232+{
233+#if defined(_TTHREAD_WIN32_)
234+ int ret;
235+
236+ if (!mtx->mTimed)
237+ {
238+ ret = TryEnterCriticalSection(&(mtx->mHandle.cs)) ? thrd_success : thrd_busy;
239+ }
240+ else
241+ {
242+ ret = (WaitForSingleObject(mtx->mHandle.mut, 0) == WAIT_OBJECT_0) ? thrd_success : thrd_busy;
243+ }
244+
245+ if ((!mtx->mRecursive) && (ret == thrd_success))
246+ {
247+ if (mtx->mAlreadyLocked)
248+ {
249+ LeaveCriticalSection(&(mtx->mHandle.cs));
250+ ret = thrd_busy;
251+ }
252+ else
253+ {
254+ mtx->mAlreadyLocked = TRUE;
255+ }
256+ }
257+ return ret;
258+#else
259+ return (pthread_mutex_trylock(mtx) == 0) ? thrd_success : thrd_busy;
260+#endif
261+}
262+
263+int mtx_unlock(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 thrd_error;
276+ }
277+ }
278+ return thrd_success;
279+#else
280+ return pthread_mutex_unlock(mtx) == 0 ? thrd_success : 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+
289+int cnd_init(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 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 thrd_error;
310+ }
311+
312+ return thrd_success;
313+#else
314+ return pthread_cond_init(cond, NULL) == 0 ? thrd_success : thrd_error;
315+#endif
316+}
317+
318+void cnd_destroy(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+
335+int cnd_signal(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 thrd_error;
351+ }
352+ }
353+
354+ return thrd_success;
355+#else
356+ return pthread_cond_signal(cond) == 0 ? thrd_success : thrd_error;
357+#endif
358+}
359+
360+int cnd_broadcast(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 thrd_error;
376+ }
377+ }
378+
379+ return thrd_success;
380+#else
381+ return pthread_cond_broadcast(cond) == 0 ? thrd_success : thrd_error;
382+#endif
383+}
384+
385+#if defined(_TTHREAD_WIN32_)
386+static int _cnd_timedwait_win32(cnd_t *cond, 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+ 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+ mtx_lock(mtx);
407+ return 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+ mtx_lock(mtx);
413+ return 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+ mtx_lock(mtx);
430+ return thrd_error;
431+ }
432+ }
433+
434+ /* Re-acquire the mutex */
435+ mtx_lock(mtx);
436+
437+ return thrd_success;
438+}
439+#endif
440+
441+int cnd_wait(cnd_t *cond, 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 ? thrd_success : thrd_error;
447+#endif
448+}
449+
450+int cnd_timedwait(cnd_t *cond, 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 thrd_error;
464+#else
465+ int ret;
466+ ret = pthread_cond_timedwait(cond, mtx, ts);
467+ if (ret == ETIMEDOUT)
468+ {
469+ return thrd_timedout;
470+ }
471+ return ret == 0 ? thrd_success : thrd_error;
472+#endif
473+}
474+
475+#if defined(_TTHREAD_WIN32_)
476+struct TinyCThreadTSSData {
477+ void* value;
478+ tss_t key;
479+ struct TinyCThreadTSSData* next;
480+};
481+
482+static 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+
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 < 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+
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+ 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_)
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+ 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+
592+int thrd_create(thrd_t *thr, 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 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 thrd_error;
619+ }
620+
621+ return thrd_success;
622+}
623+
624+thrd_t thrd_current(void)
625+{
626+#if defined(_TTHREAD_WIN32_)
627+ return GetCurrentThread();
628+#else
629+ return pthread_self();
630+#endif
631+}
632+
633+int thrd_detach(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 ? thrd_success : thrd_error;
638+#else
639+ return pthread_detach(thr) == 0 ? thrd_success : thrd_error;
640+#endif
641+}
642+
643+int thrd_equal(thrd_t thr0, 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+
652+void 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+
666+int thrd_join(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 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 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 thrd_error;
692+ }
693+ if (res != NULL)
694+ {
695+ *res = (int)(intptr_t)pres;
696+ }
697+#endif
698+ return thrd_success;
699+}
700+
701+int 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+
742+void thrd_yield(void)
743+{
744+#if defined(_TTHREAD_WIN32_)
745+ Sleep(0);
746+#else
747+ sched_yield();
748+#endif
749+}
750+
751+int tss_create(tss_t *key, tss_dtor_t dtor)
752+{
753+#if defined(_TTHREAD_WIN32_)
754+ *key = TlsAlloc();
755+ if (*key == TLS_OUT_OF_INDEXES)
756+ {
757+ return thrd_error;
758+ }
759+ _tinycthread_tss_dtors[*key] = dtor;
760+#else
761+ if (pthread_key_create(key, dtor) != 0)
762+ {
763+ return thrd_error;
764+ }
765+#endif
766+ return thrd_success;
767+}
768+
769+void tss_delete(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+
806+void *tss_get(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+
820+int tss_set(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 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 thrd_error;
854+ }
855+ }
856+ data->value = val;
857+#else
858+ if (pthread_setspecific(key, val) != 0)
859+ {
860+ return thrd_error;
861+ }
862+#endif
863+ return thrd_success;
864+}
865+
866+#if defined(_TTHREAD_EMULATE_TIMESPEC_GET_)
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_)
897+void 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
+479,
-0
1@@ -0,0 +1,479 @@
2+/* -*- mode: c; tab-width: 2; indent-tabs-mode: nil; -*-
3+Copyright (c) 2012 Marcus Geelnard
4+Copyright (c) 2013-2016 Evan Nemerson
5+
6+This software is provided 'as-is', without any express or implied
7+warranty. In no event will the authors be held liable for any damages
8+arising from the use of this software.
9+
10+Permission is granted to anyone to use this software for any purpose,
11+including commercial applications, and to alter it and redistribute it
12+freely, subject to the following restrictions:
13+
14+ 1. The origin of this software must not be misrepresented; you must not
15+ claim that you wrote the original software. If you use this software
16+ in a product, an acknowledgment in the product documentation would be
17+ appreciated but is not required.
18+
19+ 2. Altered source versions must be plainly marked as such, and must not be
20+ misrepresented as being the original software.
21+
22+ 3. This notice may not be removed or altered from any source
23+ distribution.
24+*/
25+
26+#ifndef _TINYCTHREAD_H_
27+#define _TINYCTHREAD_H_
28+
29+#ifdef __cplusplus
30+extern "C" {
31+#endif
32+
33+/**
34+* @file
35+* @mainpage TinyCThread API Reference
36+*
37+* @section intro_sec Introduction
38+* TinyCThread is a minimal, portable implementation of basic threading
39+* classes for C.
40+*
41+* They closely mimic the functionality and naming of the C11 standard, and
42+* should be easily replaceable with the corresponding standard variants.
43+*
44+* @section port_sec Portability
45+* The Win32 variant uses the native Win32 API for implementing the thread
46+* classes, while for other systems, the POSIX threads API (pthread) is used.
47+*
48+* @section misc_sec Miscellaneous
49+* The following special keywords are available: #_Thread_local.
50+*
51+* For more detailed information, browse the different sections of this
52+* documentation. A good place to start is:
53+* tinycthread.h.
54+*/
55+
56+/* Which platform are we on? */
57+#if !defined(_TTHREAD_PLATFORM_DEFINED_)
58+ #if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__)
59+ #define _TTHREAD_WIN32_
60+ #else
61+ #define _TTHREAD_POSIX_
62+ #endif
63+ #define _TTHREAD_PLATFORM_DEFINED_
64+#endif
65+
66+/* Activate some POSIX functionality (e.g. clock_gettime and recursive mutexes) */
67+#if defined(_TTHREAD_POSIX_)
68+ #undef _FEATURES_H
69+ #if !defined(_GNU_SOURCE)
70+ #define _GNU_SOURCE
71+ #endif
72+ #if !defined(_POSIX_C_SOURCE) || ((_POSIX_C_SOURCE - 0) < 199309L)
73+ #undef _POSIX_C_SOURCE
74+ #define _POSIX_C_SOURCE 199309L
75+ #endif
76+ #if !defined(_XOPEN_SOURCE) || ((_XOPEN_SOURCE - 0) < 500)
77+ #undef _XOPEN_SOURCE
78+ #define _XOPEN_SOURCE 500
79+ #endif
80+ #define _XPG6
81+#endif
82+
83+/* Generic includes */
84+#include <time.h>
85+
86+/* Platform specific includes */
87+#if defined(_TTHREAD_POSIX_)
88+ #include <pthread.h>
89+#elif defined(_TTHREAD_WIN32_)
90+ #ifndef WIN32_LEAN_AND_MEAN
91+ #define WIN32_LEAN_AND_MEAN
92+ #define __UNDEF_LEAN_AND_MEAN
93+ #endif
94+ #include <windows.h>
95+ #ifdef __UNDEF_LEAN_AND_MEAN
96+ #undef WIN32_LEAN_AND_MEAN
97+ #undef __UNDEF_LEAN_AND_MEAN
98+ #endif
99+#endif
100+
101+/* Compiler-specific information */
102+#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
103+ #define TTHREAD_NORETURN _Noreturn
104+#elif defined(__GNUC__)
105+ #define TTHREAD_NORETURN __attribute__((__noreturn__))
106+#else
107+ #define TTHREAD_NORETURN
108+#endif
109+
110+/* If TIME_UTC is missing, provide it and provide a wrapper for
111+ timespec_get. */
112+#ifndef TIME_UTC
113+#define TIME_UTC 1
114+#define _TTHREAD_EMULATE_TIMESPEC_GET_
115+
116+#if defined(_TTHREAD_WIN32_)
117+struct _tthread_timespec {
118+ time_t tv_sec;
119+ long tv_nsec;
120+};
121+#define timespec _tthread_timespec
122+#endif
123+
124+int _tthread_timespec_get(struct timespec *ts, int base);
125+#define timespec_get _tthread_timespec_get
126+#endif
127+
128+/** TinyCThread version (major number). */
129+#define TINYCTHREAD_VERSION_MAJOR 1
130+/** TinyCThread version (minor number). */
131+#define TINYCTHREAD_VERSION_MINOR 2
132+/** TinyCThread version (full version). */
133+#define TINYCTHREAD_VERSION (TINYCTHREAD_VERSION_MAJOR * 100 + TINYCTHREAD_VERSION_MINOR)
134+
135+/**
136+* @def _Thread_local
137+* Thread local storage keyword.
138+* A variable that is declared with the @c _Thread_local keyword makes the
139+* value of the variable local to each thread (known as thread-local storage,
140+* or TLS). Example usage:
141+* @code
142+* // This variable is local to each thread.
143+* _Thread_local int variable;
144+* @endcode
145+* @note The @c _Thread_local keyword is a macro that maps to the corresponding
146+* compiler directive (e.g. @c __declspec(thread)).
147+* @note This directive is currently not supported on Mac OS X (it will give
148+* a compiler error), since compile-time TLS is not supported in the Mac OS X
149+* executable format. Also, some older versions of MinGW (before GCC 4.x) do
150+* not support this directive, nor does the Tiny C Compiler.
151+* @hideinitializer
152+*/
153+
154+#if !(defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201102L)) && !defined(_Thread_local)
155+ #if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__SUNPRO_CC) || defined(__IBMCPP__)
156+ #define _Thread_local __thread
157+ #else
158+ #define _Thread_local __declspec(thread)
159+ #endif
160+#elif defined(__GNUC__) && defined(__GNUC_MINOR__) && (((__GNUC__ << 8) | __GNUC_MINOR__) < ((4 << 8) | 9))
161+ #define _Thread_local __thread
162+#endif
163+
164+/* Macros */
165+#if defined(_TTHREAD_WIN32_)
166+#define TSS_DTOR_ITERATIONS (4)
167+#else
168+#define TSS_DTOR_ITERATIONS PTHREAD_DESTRUCTOR_ITERATIONS
169+#endif
170+
171+/* Function return values */
172+#define thrd_error 0 /**< The requested operation failed */
173+#define thrd_success 1 /**< The requested operation succeeded */
174+#define thrd_timedout 2 /**< The time specified in the call was reached without acquiring the requested resource */
175+#define thrd_busy 3 /**< The requested operation failed because a tesource requested by a test and return function is already in use */
176+#define thrd_nomem 4 /**< The requested operation failed because it was unable to allocate memory */
177+
178+/* Mutex types */
179+#define mtx_plain 0
180+#define mtx_timed 1
181+#define mtx_recursive 2
182+
183+/* Mutex */
184+#if defined(_TTHREAD_WIN32_)
185+typedef struct {
186+ union {
187+ CRITICAL_SECTION cs; /* Critical section handle (used for non-timed mutexes) */
188+ HANDLE mut; /* Mutex handle (used for timed mutex) */
189+ } mHandle; /* Mutex handle */
190+ int mAlreadyLocked; /* TRUE if the mutex is already locked */
191+ int mRecursive; /* TRUE if the mutex is recursive */
192+ int mTimed; /* TRUE if the mutex is timed */
193+} mtx_t;
194+#else
195+typedef pthread_mutex_t mtx_t;
196+#endif
197+
198+/** Create a mutex object.
199+* @param mtx A mutex object.
200+* @param type Bit-mask that must have one of the following six values:
201+* @li @c mtx_plain for a simple non-recursive mutex
202+* @li @c mtx_timed for a non-recursive mutex that supports timeout
203+* @li @c mtx_plain | @c mtx_recursive (same as @c mtx_plain, but recursive)
204+* @li @c mtx_timed | @c mtx_recursive (same as @c mtx_timed, but recursive)
205+* @return @ref thrd_success on success, or @ref thrd_error if the request could
206+* not be honored.
207+*/
208+int mtx_init(mtx_t *mtx, int type);
209+
210+/** Release any resources used by the given mutex.
211+* @param mtx A mutex object.
212+*/
213+void mtx_destroy(mtx_t *mtx);
214+
215+/** Lock the given mutex.
216+* Blocks until the given mutex can be locked. If the mutex is non-recursive, and
217+* the calling thread already has a lock on the mutex, this call will block
218+* forever.
219+* @param mtx A mutex object.
220+* @return @ref thrd_success on success, or @ref thrd_error if the request could
221+* not be honored.
222+*/
223+int mtx_lock(mtx_t *mtx);
224+
225+/** Lock the given mutex, or block until a specific point in time.
226+* Blocks until either the given mutex can be locked, or the specified TIME_UTC
227+* based time.
228+* @param mtx A mutex object.
229+* @param ts A UTC based calendar time
230+* @return @ref The mtx_timedlock function returns thrd_success on success, or
231+* thrd_timedout if the time specified was reached without acquiring the
232+* requested resource, or thrd_error if the request could not be honored.
233+*/
234+int mtx_timedlock(mtx_t *mtx, const struct timespec *ts);
235+
236+/** Try to lock the given mutex.
237+* The specified mutex shall support either test and return or timeout. If the
238+* mutex is already locked, the function returns without blocking.
239+* @param mtx A mutex object.
240+* @return @ref thrd_success on success, or @ref thrd_busy if the resource
241+* requested is already in use, or @ref thrd_error if the request could not be
242+* honored.
243+*/
244+int mtx_trylock(mtx_t *mtx);
245+
246+/** Unlock the given mutex.
247+* @param mtx A mutex object.
248+* @return @ref thrd_success on success, or @ref thrd_error if the request could
249+* not be honored.
250+*/
251+int mtx_unlock(mtx_t *mtx);
252+
253+/* Condition variable */
254+#if defined(_TTHREAD_WIN32_)
255+typedef struct {
256+ HANDLE mEvents[2]; /* Signal and broadcast event HANDLEs. */
257+ unsigned int mWaitersCount; /* Count of the number of waiters. */
258+ CRITICAL_SECTION mWaitersCountLock; /* Serialize access to mWaitersCount. */
259+} cnd_t;
260+#else
261+typedef pthread_cond_t cnd_t;
262+#endif
263+
264+/** Create a condition variable object.
265+* @param cond A condition variable object.
266+* @return @ref thrd_success on success, or @ref thrd_error if the request could
267+* not be honored.
268+*/
269+int cnd_init(cnd_t *cond);
270+
271+/** Release any resources used by the given condition variable.
272+* @param cond A condition variable object.
273+*/
274+void cnd_destroy(cnd_t *cond);
275+
276+/** Signal a condition variable.
277+* Unblocks one of the threads that are blocked on the given condition variable
278+* at the time of the call. If no threads are blocked on the condition variable
279+* at the time of the call, the function does nothing and return success.
280+* @param cond A condition variable object.
281+* @return @ref thrd_success on success, or @ref thrd_error if the request could
282+* not be honored.
283+*/
284+int cnd_signal(cnd_t *cond);
285+
286+/** Broadcast a condition variable.
287+* Unblocks all of the threads that are blocked on the given condition variable
288+* at the time of the call. If no threads are blocked on the condition variable
289+* at the time of the call, the function does nothing and return success.
290+* @param cond A condition variable object.
291+* @return @ref thrd_success on success, or @ref thrd_error if the request could
292+* not be honored.
293+*/
294+int cnd_broadcast(cnd_t *cond);
295+
296+/** Wait for a condition variable to become signaled.
297+* The function atomically unlocks the given mutex and endeavors to block until
298+* the given condition variable is signaled by a call to cnd_signal or to
299+* cnd_broadcast. When the calling thread becomes unblocked it locks the mutex
300+* before it returns.
301+* @param cond A condition variable object.
302+* @param mtx A mutex object.
303+* @return @ref thrd_success on success, or @ref thrd_error if the request could
304+* not be honored.
305+*/
306+int cnd_wait(cnd_t *cond, mtx_t *mtx);
307+
308+/** Wait for a condition variable to become signaled.
309+* The function atomically unlocks the given mutex and endeavors to block until
310+* the given condition variable is signaled by a call to cnd_signal or to
311+* cnd_broadcast, or until after the specified time. When the calling thread
312+* becomes unblocked it locks the mutex before it returns.
313+* @param cond A condition variable object.
314+* @param mtx A mutex object.
315+* @param xt A point in time at which the request will time out (absolute time).
316+* @return @ref thrd_success upon success, or @ref thrd_timeout if the time
317+* specified in the call was reached without acquiring the requested resource, or
318+* @ref thrd_error if the request could not be honored.
319+*/
320+int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts);
321+
322+/* Thread */
323+#if defined(_TTHREAD_WIN32_)
324+typedef HANDLE thrd_t;
325+#else
326+typedef pthread_t thrd_t;
327+#endif
328+
329+/** Thread start function.
330+* Any thread that is started with the @ref thrd_create() function must be
331+* started through a function of this type.
332+* @param arg The thread argument (the @c arg argument of the corresponding
333+* @ref thrd_create() call).
334+* @return The thread return value, which can be obtained by another thread
335+* by using the @ref thrd_join() function.
336+*/
337+typedef int (*thrd_start_t)(void *arg);
338+
339+/** Create a new thread.
340+* @param thr Identifier of the newly created thread.
341+* @param func A function pointer to the function that will be executed in
342+* the new thread.
343+* @param arg An argument to the thread function.
344+* @return @ref thrd_success on success, or @ref thrd_nomem if no memory could
345+* be allocated for the thread requested, or @ref thrd_error if the request
346+* could not be honored.
347+* @note A thread’s identifier may be reused for a different thread once the
348+* original thread has exited and either been detached or joined to another
349+* thread.
350+*/
351+int thrd_create(thrd_t *thr, thrd_start_t func, void *arg);
352+
353+/** Identify the calling thread.
354+* @return The identifier of the calling thread.
355+*/
356+thrd_t thrd_current(void);
357+
358+/** Dispose of any resources allocated to the thread when that thread exits.
359+ * @return thrd_success, or thrd_error on error
360+*/
361+int thrd_detach(thrd_t thr);
362+
363+/** Compare two thread identifiers.
364+* The function determines if two thread identifiers refer to the same thread.
365+* @return Zero if the two thread identifiers refer to different threads.
366+* Otherwise a nonzero value is returned.
367+*/
368+int thrd_equal(thrd_t thr0, thrd_t thr1);
369+
370+/** Terminate execution of the calling thread.
371+* @param res Result code of the calling thread.
372+*/
373+TTHREAD_NORETURN void thrd_exit(int res);
374+
375+/** Wait for a thread to terminate.
376+* The function joins the given thread with the current thread by blocking
377+* until the other thread has terminated.
378+* @param thr The thread to join with.
379+* @param res If this pointer is not NULL, the function will store the result
380+* code of the given thread in the integer pointed to by @c res.
381+* @return @ref thrd_success on success, or @ref thrd_error if the request could
382+* not be honored.
383+*/
384+int thrd_join(thrd_t thr, int *res);
385+
386+/** Put the calling thread to sleep.
387+* Suspend execution of the calling thread.
388+* @param duration Interval to sleep for
389+* @param remaining If non-NULL, this parameter will hold the remaining
390+* time until time_point upon return. This will
391+* typically be zero, but if the thread was woken up
392+* by a signal that is not ignored before duration was
393+* reached @c remaining will hold a positive time.
394+* @return 0 (zero) on successful sleep, -1 if an interrupt occurred,
395+* or a negative value if the operation fails.
396+*/
397+int thrd_sleep(const struct timespec *duration, struct timespec *remaining);
398+
399+/** Yield execution to another thread.
400+* Permit other threads to run, even if the current thread would ordinarily
401+* continue to run.
402+*/
403+void thrd_yield(void);
404+
405+/* Thread local storage */
406+#if defined(_TTHREAD_WIN32_)
407+typedef DWORD tss_t;
408+#else
409+typedef pthread_key_t tss_t;
410+#endif
411+
412+/** Destructor function for a thread-specific storage.
413+* @param val The value of the destructed thread-specific storage.
414+*/
415+typedef void (*tss_dtor_t)(void *val);
416+
417+/** Create a thread-specific storage.
418+* @param key The unique key identifier that will be set if the function is
419+* successful.
420+* @param dtor Destructor function. This can be NULL.
421+* @return @ref thrd_success on success, or @ref thrd_error if the request could
422+* not be honored.
423+* @note On Windows, the @c dtor will definitely be called when
424+* appropriate for threads created with @ref thrd_create. It will be
425+* called for other threads in most cases, the possible exception being
426+* for DLLs loaded with LoadLibraryEx. In order to be certain, you
427+* should use @ref thrd_create whenever possible.
428+*/
429+int tss_create(tss_t *key, tss_dtor_t dtor);
430+
431+/** Delete a thread-specific storage.
432+* The function releases any resources used by the given thread-specific
433+* storage.
434+* @param key The key that shall be deleted.
435+*/
436+void tss_delete(tss_t key);
437+
438+/** Get the value for a thread-specific storage.
439+* @param key The thread-specific storage identifier.
440+* @return The value for the current thread held in the given thread-specific
441+* storage.
442+*/
443+void *tss_get(tss_t key);
444+
445+/** Set the value for a thread-specific storage.
446+* @param key The thread-specific storage identifier.
447+* @param val The value of the thread-specific storage to set for the current
448+* thread.
449+* @return @ref thrd_success on success, or @ref thrd_error if the request could
450+* not be honored.
451+*/
452+int tss_set(tss_t key, void *val);
453+
454+#if defined(_TTHREAD_WIN32_)
455+ typedef struct {
456+ LONG volatile status;
457+ CRITICAL_SECTION lock;
458+ } once_flag;
459+ #define ONCE_FLAG_INIT {0,}
460+#else
461+ #define once_flag pthread_once_t
462+ #define ONCE_FLAG_INIT PTHREAD_ONCE_INIT
463+#endif
464+
465+/** Invoke a callback exactly once
466+ * @param flag Flag used to ensure the callback is invoked exactly
467+ * once.
468+ * @param func Callback to invoke.
469+ */
470+#if defined(_TTHREAD_WIN32_)
471+ void call_once(once_flag *flag, void (*func)(void));
472+#else
473+ #define call_once(flag,func) pthread_once(flag,func)
474+#endif
475+
476+#ifdef __cplusplus
477+}
478+#endif
479+
480+#endif /* _TINYTHREAD_H_ */
+10,
-10
1@@ -1,7 +1,6 @@
2 #include <errno.h>
3 #include <fcntl.h>
4 #include <limits.h>
5-#include <pthread.h>
6 #include <stddef.h>
7 #include <stdint.h>
8 #include <stdio.h>
9@@ -11,6 +10,7 @@
10 #include <sys/stat.h>
11 #include <sys/types.h>
12 #include <time.h>
13+#include <tinycthread.h>
14 #include <unistd.h>
15
16 #include "config.h"
17@@ -37,7 +37,7 @@ static int stat_item(const struct item* it, struct stat* st);
18 static int stream_file(int c, const struct item* it, const char* hdr, int head_only);
19
20 extern struct library lib;
21-extern pthread_rwlock_t lib_lock;
22+extern mtx_t lib_lock;
23
24 static int cors_origin_allowed(const char* origin)
25 {
26@@ -142,7 +142,7 @@ static int item_path_for_id(char out[4096], uint64_t id, const struct user* u)
27
28 out[0] = '\0';
29
30- pthread_rwlock_rdlock(&lib_lock);
31+ mtx_lock(&lib_lock);
32
33 const struct item* it = find_item(id);
34 if (!it) {
35@@ -163,7 +163,7 @@ static int item_path_for_id(char out[4096], uint64_t id, const struct user* u)
36 ret = 0;
37
38 out:
39- pthread_rwlock_unlock(&lib_lock);
40+ mtx_unlock(&lib_lock);
41 return ret; /* 0 ok, 1 not found, -1 error */
42 }
43
44@@ -654,7 +654,7 @@ int http_handle(int c)
45 if (strcmp(path, "/library") == 0) {
46 LOG(verbose_log, "HTTP", "Route /library");
47
48- pthread_rwlock_rdlock(&lib_lock);
49+ mtx_lock(&lib_lock);
50
51 struct json j;
52 struct library view;
53@@ -667,7 +667,7 @@ int http_handle(int c)
54 if (lib.len > 0) {
55 view.items = calloc(lib.len, sizeof(*view.items));
56 if (!view.items) {
57- pthread_rwlock_unlock(&lib_lock);
58+ mtx_unlock(&lib_lock);
59 reply_text(c, hdr, HTTP_500, "Server Error\n");
60 return -1;
61 }
62@@ -685,14 +685,14 @@ int http_handle(int c)
63 if (u)
64 free(view.items);
65
66- pthread_rwlock_unlock(&lib_lock);
67+ mtx_unlock(&lib_lock);
68
69 LOG(verbose_log, "JSON", "Encode FAILED");
70 reply_text(c, hdr, HTTP_500, "JSON Encode Failed\n");
71 return -1;
72 }
73
74- pthread_rwlock_unlock(&lib_lock);
75+ mtx_unlock(&lib_lock);
76
77 LOG(verbose_log, "JSON", "Encoded bytes %zu bytes", j.len);
78
79@@ -723,7 +723,7 @@ int http_handle(int c)
80 size_t before = 0;
81 size_t after = 0;
82
83- pthread_rwlock_wrlock(&lib_lock);
84+ mtx_lock(&lib_lock);
85
86 before = lib.len;
87 LOG(verbose_log, "SCAN", "Rescan begin %s (%zu items)", media_dir, before);
88@@ -732,7 +732,7 @@ int http_handle(int c)
89
90 after = lib.len;
91
92- pthread_rwlock_unlock(&lib_lock);
93+ mtx_unlock(&lib_lock);
94
95 clock_gettime(CLOCK_MONOTONIC, &t1);
96
+13,
-10
1@@ -10,7 +10,6 @@
2
3 #include <errno.h>
4 #include <fcntl.h>
5-#include <pthread.h>
6 #include <semaphore.h>
7 #include <signal.h>
8 #include <stdarg.h>
9@@ -28,6 +27,7 @@
10
11 #include <arpa/inet.h>
12 #include <netinet/in.h>
13+#include <tinycthread.h>
14
15 #include "config.h"
16 #include "http.h"
17@@ -40,7 +40,7 @@
18 #endif /* GIT_VER */
19
20 static void apply_rlimits(void);
21-static void* client_thread(void* arg);
22+static int client_thread(void* arg);
23 static void fd_set_cloexec(int fd);
24 static void sock_set_timeouts(int fd);
25
26@@ -51,7 +51,7 @@ void setup(void);
27 static sem_t slots;
28 static int sock;
29 struct library lib;
30-pthread_rwlock_t lib_lock = PTHREAD_RWLOCK_INITIALIZER;
31+mtx_t lib_lock;
32
33 static void apply_rlimits(void)
34 {
35@@ -66,7 +66,7 @@ static void apply_rlimits(void)
36 (void)setrlimit(RLIMIT_NOFILE, &rl);
37 }
38
39-static void* client_thread(void* arg)
40+static int client_thread(void* arg)
41 {
42 int c = (int)(intptr_t)arg;
43
44@@ -76,7 +76,7 @@ static void* client_thread(void* arg)
45 close(c);
46
47 (void)sem_post(&slots);
48- return NULL;
49+ return 0;
50 }
51
52 static void fd_set_cloexec(int fd)
53@@ -135,16 +135,16 @@ void run(void)
54 }
55
56 LOG(verbose_log, "CORE", "Connection Accepted");
57- pthread_t t;
58- int err = pthread_create(&t, NULL, client_thread, (void*)(intptr_t)c);
59- if (err != 0) {
60- LOG(true, "CORE", "pthread_create FAILED %d", err);
61+ thrd_t t;
62+ int err = thrd_create(&t, client_thread, (void*)(intptr_t)c);
63+ if (err != thrd_success) {
64+ LOG(true, "CORE", "thrd_create FAILED %d", err);
65 close(c);
66 (void)sem_post(&slots);
67 continue;
68 }
69
70- (void)pthread_detach(t);
71+ (void)thrd_detach(t);
72 }
73 }
74
75@@ -155,6 +155,9 @@ void setup(void)
76 config_load();
77 apply_rlimits();
78
79+ if (mtx_init(&lib_lock, mtx_plain) != thrd_success)
80+ die("mtx_init", EXIT_FAILURE);
81+
82 if (sem_init(&slots, 0, max_clients) < 0)
83 die("sem_init", EXIT_FAILURE);
84
+0,
-1
1@@ -1,4 +1,3 @@
2-#include <pthread.h>
3 #include <stdbool.h>
4 #include <stdint.h>
5 #include <stdio.h>