commit 1327bb8

uint  ·  2026-03-09 17:06:24 +0000 UTC
parent f68374b
migrate pthreads->tinycthread
7 files changed,  +1457, -24
+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(&current_ts, TIME_UTC);
148+
149+  if ((current_ts.tv_sec > ts->tv_sec) || ((current_ts.tv_sec == ts->tv_sec) && (current_ts.tv_nsec >= ts->tv_nsec)))
150+  {
151+    timeoutMs = 0;
152+  }
153+  else
154+  {
155+    timeoutMs  = (DWORD)(ts->tv_sec  - current_ts.tv_sec)  * 1000;
156+    timeoutMs += (ts->tv_nsec - current_ts.tv_nsec) / 1000000;
157+    timeoutMs += 1;
158+  }
159+
160+  /* TODO: the timeout for WaitForSingleObject doesn't include time
161+     while the computer is asleep. */
162+  switch (WaitForSingleObject(mtx->mHandle.mut, timeoutMs))
163+  {
164+    case WAIT_OBJECT_0:
165+      break;
166+    case WAIT_TIMEOUT:
167+      return 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>