From 5f80c8b54d011580383810597aa68565ddfb8f3c Mon Sep 17 00:00:00 2001
From: Mitya Selivanov <0x7fffff@guattari.ru>
Date: Thu, 18 Aug 2022 06:50:13 +0400
Subject: [test] thread, mutex, condition variable

---
 LICENSE                                            |   2 +
 README.md                                          |  11 +-
 source/kit/CMakeLists.txt                          |  11 +-
 source/kit/c11/CMakeLists.txt                      |   6 -
 source/kit/c11/impl/CMakeLists.txt                 |   7 -
 source/kit/c11/impl/threads_posix.c                | 313 --------------
 source/kit/c11/impl/threads_win32.c                | 460 --------------------
 source/kit/c11/impl/threads_win32.h                |  21 -
 source/kit/c11/impl/threads_win32_tls_callback.cpp |  18 -
 source/kit/c11/impl/time.c                         |  84 ----
 source/kit/c11/threads.h                           | 198 ---------
 source/kit/c11/time.h                              |  62 ---
 source/kit/mersenne_twister_64.c                   |   2 +-
 source/kit/threads.h                               | 201 +++++++++
 source/kit/threads.posix.c                         | 315 ++++++++++++++
 source/kit/threads.win32.c                         | 462 +++++++++++++++++++++
 source/kit/threads.win32.h                         |  25 ++
 source/kit/threads_tls_callback.win32.cpp          |  21 +
 source/kit/time.c                                  |  84 ++++
 source/kit/time.h                                  |  62 +++
 source/test/unittests/CMakeLists.txt               |   7 +-
 source/test/unittests/c11_threads.c                |   6 -
 source/test/unittests/condition_variable.test.c    |  60 +++
 source/test/unittests/mutex.test.c                 |  74 ++++
 source/test/unittests/thread.test.c                |  78 ++++
 25 files changed, 1406 insertions(+), 1184 deletions(-)
 delete mode 100644 source/kit/c11/CMakeLists.txt
 delete mode 100644 source/kit/c11/impl/CMakeLists.txt
 delete mode 100644 source/kit/c11/impl/threads_posix.c
 delete mode 100644 source/kit/c11/impl/threads_win32.c
 delete mode 100644 source/kit/c11/impl/threads_win32.h
 delete mode 100644 source/kit/c11/impl/threads_win32_tls_callback.cpp
 delete mode 100644 source/kit/c11/impl/time.c
 delete mode 100644 source/kit/c11/threads.h
 delete mode 100644 source/kit/c11/time.h
 create mode 100644 source/kit/threads.h
 create mode 100644 source/kit/threads.posix.c
 create mode 100644 source/kit/threads.win32.c
 create mode 100644 source/kit/threads.win32.h
 create mode 100644 source/kit/threads_tls_callback.win32.cpp
 create mode 100644 source/kit/time.c
 create mode 100644 source/kit/time.h
 delete mode 100644 source/test/unittests/c11_threads.c
 create mode 100644 source/test/unittests/condition_variable.test.c
 create mode 100644 source/test/unittests/mutex.test.c
 create mode 100644 source/test/unittests/thread.test.c

diff --git a/LICENSE b/LICENSE
index 07dde7d..8959d98 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,3 +1,5 @@
+Copyright (c) 2012 yohhoy
+Copyright (c) 2022 Yonggang Luo
 Copyright (c) 2022 Mitya Selivanov
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
diff --git a/README.md b/README.md
index 83935bf..0e59887 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,16 @@
 # kit
+
 - Unit-testing
-- Async function
 - Lower bound
 - Dynamic array
 - Input buffer
 - Random number generation
+- Async function
+- Atomic
+- Condition variable
+- Mutual exclusion
+- Thread
+
+Condition variables, mutual exclusions and threads implementation was forked from [Mesa][mesa_link] source code.
+
+[mesa_link]: https://gitlab.freedesktop.org/mesa/mesa
diff --git a/source/kit/CMakeLists.txt b/source/kit/CMakeLists.txt
index 397b404..86c39dc 100644
--- a/source/kit/CMakeLists.txt
+++ b/source/kit/CMakeLists.txt
@@ -1,18 +1,21 @@
 target_sources(
   ${KIT_LIBRARY}
     PRIVATE
-      atomic.c input_buffer.c input_stream.c lower_bound.c
-      string_ref.c async_function.c allocator.c array_ref.c dynamic_array.c
-      mersenne_twister_64.c
+      atomic.c input_buffer.c threads.win32.c time.c
+      threads.posix.c input_stream.c lower_bound.c string_ref.c
+      async_function.c allocator.c array_ref.c dynamic_array.c
+      mersenne_twister_64.c threads_tls_callback.win32.cpp
     PUBLIC
+      $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/time.h>
       $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/atomic.h>
       $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/allocator.h>
       $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/string_ref.h>
+      $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/threads.h>
       $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/dynamic_array.h>
       $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/async_function.h>
+      $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/threads.win32.h>
       $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/input_stream.h>
       $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/input_buffer.h>
       $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/lower_bound.h>
       $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/array_ref.h>
       $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/mersenne_twister_64.h>)
-add_subdirectory(c11)
diff --git a/source/kit/c11/CMakeLists.txt b/source/kit/c11/CMakeLists.txt
deleted file mode 100644
index 205b256..0000000
--- a/source/kit/c11/CMakeLists.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-target_sources(
-  ${KIT_LIBRARY}
-    PUBLIC
-      $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/time.h>
-      $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/threads.h>)
-add_subdirectory(impl)
diff --git a/source/kit/c11/impl/CMakeLists.txt b/source/kit/c11/impl/CMakeLists.txt
deleted file mode 100644
index c64b895..0000000
--- a/source/kit/c11/impl/CMakeLists.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-target_sources(
-  ${KIT_LIBRARY}
-    PRIVATE
-      time.c threads_posix.c threads_win32.c
-      threads_win32_tls_callback.cpp
-    PUBLIC
-      $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/threads_win32.h>)
diff --git a/source/kit/c11/impl/threads_posix.c b/source/kit/c11/impl/threads_posix.c
deleted file mode 100644
index ebf411c..0000000
--- a/source/kit/c11/impl/threads_posix.c
+++ /dev/null
@@ -1,313 +0,0 @@
-/*
- * C11 <threads.h> emulation library
- *
- * (C) Copyright yohhoy 2012.
- * Distributed under the Boost Software License, Version 1.0.
- *
- * Permission is hereby granted, free of charge, to any person or
- * organization obtaining a copy of the software and accompanying
- * documentation covered by this license (the "Software") to use,
- * reproduce, display, distribute, execute, and transmit the Software,
- * and to prepare [[derivative work]]s of the Software, and to permit
- * third-parties to whom the Software is furnished to do so, all
- * subject to the following:
- *
- * The copyright notices in the Software and this entire statement,
- * including the above license grant, this restriction and the
- * following disclaimer, must be included in all copies of the
- * Software, in whole or in part, and all derivative works of the
- * Software, unless such copies or derivative works are solely in the
- * form of machine-executable object code generated by a source
- * language processor.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
- * NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE
- * DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER
- * LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-#ifndef KIT_HAVE_WINDOWS
-
-#  include <assert.h>
-#  include <errno.h>
-#  include <limits.h>
-#  include <sched.h>
-#  include <stdint.h> /* for intptr_t */
-#  include <stdlib.h>
-#  include <unistd.h>
-
-#  include "../threads.h"
-
-/*
-Configuration macro:
-
-  EMULATED_THREADS_USE_NATIVE_TIMEDLOCK
-    Use pthread_mutex_timedlock() for `mtx_timedlock()'
-    Otherwise use mtx_trylock() + *busy loop* emulation.
-*/
-#  if !defined(__CYGWIN__) && !defined(__APPLE__) && \
-      !defined(__NetBSD__)
-#    define EMULATED_THREADS_USE_NATIVE_TIMEDLOCK
-#  endif
-
-/*---------------------------- types ----------------------------*/
-
-/*
-Implementation limits:
-  - Conditionally emulation for "mutex with timeout"
-    (see EMULATED_THREADS_USE_NATIVE_TIMEDLOCK macro)
-*/
-struct impl_thrd_param {
-  thrd_start_t func;
-  void        *arg;
-};
-
-static void *impl_thrd_routine(void *p) {
-  struct impl_thrd_param pack = *((struct impl_thrd_param *) p);
-  free(p);
-  return (void *) (intptr_t) pack.func(pack.arg);
-}
-
-/*--------------- 7.25.2 Initialization functions ---------------*/
-// 7.25.2.1
-void call_once(once_flag *flag, void (*func)(void)) {
-  pthread_once(flag, func);
-}
-
-/*------------- 7.25.3 Condition variable functions -------------*/
-// 7.25.3.1
-int cnd_broadcast(cnd_t *cond) {
-  assert(cond != NULL);
-  return (pthread_cond_broadcast(cond) == 0) ? thrd_success
-                                             : thrd_error;
-}
-
-// 7.25.3.2
-void cnd_destroy(cnd_t *cond) {
-  assert(cond);
-  pthread_cond_destroy(cond);
-}
-
-// 7.25.3.3
-int cnd_init(cnd_t *cond) {
-  assert(cond != NULL);
-  return (pthread_cond_init(cond, NULL) == 0) ? thrd_success
-                                              : thrd_error;
-}
-
-// 7.25.3.4
-int cnd_signal(cnd_t *cond) {
-  assert(cond != NULL);
-  return (pthread_cond_signal(cond) == 0) ? thrd_success : thrd_error;
-}
-
-// 7.25.3.5
-int cnd_timedwait(cnd_t *cond, mtx_t *mtx,
-                  const struct timespec *abs_time) {
-  int rt;
-
-  assert(mtx != NULL);
-  assert(cond != NULL);
-  assert(abs_time != NULL);
-
-  rt = pthread_cond_timedwait(cond, mtx, abs_time);
-  if (rt == ETIMEDOUT)
-    return thrd_timedout;
-  return (rt == 0) ? thrd_success : thrd_error;
-}
-
-// 7.25.3.6
-int cnd_wait(cnd_t *cond, mtx_t *mtx) {
-  assert(mtx != NULL);
-  assert(cond != NULL);
-  return (pthread_cond_wait(cond, mtx) == 0) ? thrd_success
-                                             : thrd_error;
-}
-
-/*-------------------- 7.25.4 Mutex functions --------------------*/
-// 7.25.4.1
-void mtx_destroy(mtx_t *mtx) {
-  assert(mtx != NULL);
-  pthread_mutex_destroy(mtx);
-}
-
-/*
- * XXX: Workaround when building with -O0 and without pthreads link.
- *
- * In such cases constant folding and dead code elimination won't be
- * available, thus the compiler will always add the pthread_mutexattr*
- * functions into the binary. As we try to link, we'll fail as the
- * symbols are unresolved.
- *
- * Ideally we'll enable the optimisations locally, yet that does not
- * seem to work.
- *
- * So the alternative workaround is to annotate the symbols as weak.
- * Thus the linker will be happy and things don't clash when building
- * with -O1 or greater.
- */
-#  if defined(HAVE_FUNC_ATTRIBUTE_WEAK) && !defined(__CYGWIN__)
-__attribute__((weak)) int pthread_mutexattr_init(
-    pthread_mutexattr_t *attr);
-
-__attribute__((weak)) int pthread_mutexattr_settype(
-    pthread_mutexattr_t *attr, int type);
-
-__attribute__((weak)) int pthread_mutexattr_destroy(
-    pthread_mutexattr_t *attr);
-#  endif
-
-// 7.25.4.2
-int mtx_init(mtx_t *mtx, int type) {
-  pthread_mutexattr_t attr;
-  assert(mtx != NULL);
-  if (type != mtx_plain && type != mtx_timed &&
-      type != (mtx_plain | mtx_recursive) &&
-      type != (mtx_timed | mtx_recursive))
-    return thrd_error;
-
-  if ((type & mtx_recursive) == 0) {
-    pthread_mutex_init(mtx, NULL);
-    return thrd_success;
-  }
-
-  pthread_mutexattr_init(&attr);
-  pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
-  pthread_mutex_init(mtx, &attr);
-  pthread_mutexattr_destroy(&attr);
-  return thrd_success;
-}
-
-// 7.25.4.3
-int mtx_lock(mtx_t *mtx) {
-  assert(mtx != NULL);
-  return (pthread_mutex_lock(mtx) == 0) ? thrd_success : thrd_error;
-}
-
-// 7.25.4.4
-int mtx_timedlock(mtx_t *mtx, const struct timespec *ts) {
-  assert(mtx != NULL);
-  assert(ts != NULL);
-
-  {
-#  ifdef EMULATED_THREADS_USE_NATIVE_TIMEDLOCK
-    int rt;
-    rt = pthread_mutex_timedlock(mtx, ts);
-    if (rt == 0)
-      return thrd_success;
-    return (rt == ETIMEDOUT) ? thrd_timedout : thrd_error;
-#  else
-    time_t expire = time(NULL);
-    expire += ts->tv_sec;
-    while (mtx_trylock(mtx) != thrd_success) {
-      time_t now = time(NULL);
-      if (expire < now)
-        return thrd_timedout;
-      // busy loop!
-      thrd_yield();
-    }
-    return thrd_success;
-#  endif
-  }
-}
-
-// 7.25.4.5
-int mtx_trylock(mtx_t *mtx) {
-  assert(mtx != NULL);
-  return (pthread_mutex_trylock(mtx) == 0) ? thrd_success : thrd_busy;
-}
-
-// 7.25.4.6
-int mtx_unlock(mtx_t *mtx) {
-  assert(mtx != NULL);
-  return (pthread_mutex_unlock(mtx) == 0) ? thrd_success : thrd_error;
-}
-
-/*------------------- 7.25.5 Thread functions -------------------*/
-// 7.25.5.1
-int thrd_create(thrd_t *thr, thrd_start_t func, void *arg) {
-  struct impl_thrd_param *pack;
-  assert(thr != NULL);
-  pack = (struct impl_thrd_param *) malloc(
-      sizeof(struct impl_thrd_param));
-  if (!pack)
-    return thrd_nomem;
-  pack->func = func;
-  pack->arg  = arg;
-  if (pthread_create(thr, NULL, impl_thrd_routine, pack) != 0) {
-    free(pack);
-    return thrd_error;
-  }
-  return thrd_success;
-}
-
-// 7.25.5.2
-thrd_t thrd_current(void) {
-  return pthread_self();
-}
-
-// 7.25.5.3
-int thrd_detach(thrd_t thr) {
-  return (pthread_detach(thr) == 0) ? thrd_success : thrd_error;
-}
-
-// 7.25.5.4
-int thrd_equal(thrd_t thr0, thrd_t thr1) {
-  return pthread_equal(thr0, thr1);
-}
-
-// 7.25.5.5
-_Noreturn void thrd_exit(int res) {
-  pthread_exit((void *) (intptr_t) res);
-}
-
-// 7.25.5.6
-int thrd_join(thrd_t thr, int *res) {
-  void *code;
-  if (pthread_join(thr, &code) != 0)
-    return thrd_error;
-  if (res)
-    *res = (int) (intptr_t) code;
-  return thrd_success;
-}
-
-// 7.25.5.7
-int thrd_sleep(const struct timespec *time_point,
-               struct timespec       *remaining) {
-  assert(time_point != NULL);
-  return nanosleep(time_point, remaining);
-}
-
-// 7.25.5.8
-void thrd_yield(void) {
-  sched_yield();
-}
-
-/*----------- 7.25.6 Thread-specific storage functions -----------*/
-// 7.25.6.1
-int tss_create(tss_t *key, tss_dtor_t dtor) {
-  assert(key != NULL);
-  return (pthread_key_create(key, dtor) == 0) ? thrd_success
-                                              : thrd_error;
-}
-
-// 7.25.6.2
-void tss_delete(tss_t key) {
-  pthread_key_delete(key);
-}
-
-// 7.25.6.3
-void *tss_get(tss_t key) {
-  return pthread_getspecific(key);
-}
-
-// 7.25.6.4
-int tss_set(tss_t key, void *val) {
-  return (pthread_setspecific(key, val) == 0) ? thrd_success
-                                              : thrd_error;
-}
-
-#endif
diff --git a/source/kit/c11/impl/threads_win32.c b/source/kit/c11/impl/threads_win32.c
deleted file mode 100644
index b94d20f..0000000
--- a/source/kit/c11/impl/threads_win32.c
+++ /dev/null
@@ -1,460 +0,0 @@
-/*
- * C11 <threads.h> emulation library
- *
- * (C) Copyright yohhoy 2012.
- * Distributed under the Boost Software License, Version 1.0.
- *
- * Permission is hereby granted, free of charge, to any person or
- * organization obtaining a copy of the software and accompanying
- * documentation covered by this license (the "Software") to use,
- * reproduce, display, distribute, execute, and transmit the Software,
- * and to prepare [[derivative work]]s of the Software, and to permit
- * third-parties to whom the Software is furnished to do so, all
- * subject to the following:
- *
- * The copyright notices in the Software and this entire statement,
- * including the above license grant, this restriction and the
- * following disclaimer, must be included in all copies of the
- * Software, in whole or in part, and all derivative works of the
- * Software, unless such copies or derivative works are solely in the
- * form of machine-executable object code generated by a source
- * language processor.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
- * NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE
- * DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER
- * LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-#ifdef KIT_HAVE_WINDOWS
-
-#  include <assert.h>
-#  include <errno.h>
-#  include <limits.h>
-#  include <process.h> // MSVCRT
-#  include <stdbool.h>
-#  include <stdlib.h>
-
-#  include "../threads.h"
-
-#  include "threads_win32.h"
-
-#  ifndef WIN32_LEAN_AND_MEAN
-#    define WIN32_LEAN_AND_MEAN 1
-#  endif
-#  include <windows.h>
-
-/*
-Configuration macro:
-
-  EMULATED_THREADS_USE_NATIVE_CALL_ONCE
-    Use native WindowsAPI one-time initialization function.
-    (requires WinVista or later)
-    Otherwise emulate by mtx_trylock() + *busy loop* for WinXP.
-
-  EMULATED_THREADS_TSS_DTOR_SLOTNUM
-    Max registerable TSS dtor number.
-*/
-
-#  if _WIN32_WINNT >= 0x0600
-// Prefer native WindowsAPI on newer environment.
-#    if !defined(__MINGW32__)
-#      define EMULATED_THREADS_USE_NATIVE_CALL_ONCE
-#    endif
-#  endif
-#  define EMULATED_THREADS_TSS_DTOR_SLOTNUM \
-    64 // see TLS_MINIMUM_AVAILABLE
-
-// check configuration
-#  if defined(EMULATED_THREADS_USE_NATIVE_CALL_ONCE) && \
-      (_WIN32_WINNT < 0x0600)
-#    error EMULATED_THREADS_USE_NATIVE_CALL_ONCE requires _WIN32_WINNT>=0x0600
-#  endif
-
-static_assert(sizeof(cnd_t) == sizeof(CONDITION_VARIABLE),
-              "The size of cnd_t must equal to CONDITION_VARIABLE");
-static_assert(sizeof(thrd_t) == sizeof(HANDLE),
-              "The size of thrd_t must equal to HANDLE");
-static_assert(sizeof(tss_t) == sizeof(DWORD),
-              "The size of tss_t must equal to DWORD");
-static_assert(sizeof(mtx_t) == sizeof(CRITICAL_SECTION),
-              "The size of mtx_t must equal to CRITICAL_SECTION");
-static_assert(sizeof(once_flag) == sizeof(INIT_ONCE),
-              "The size of once_flag must equal to INIT_ONCE");
-
-/*
-Implementation limits:
-  - Conditionally emulation for "Initialization functions"
-    (see EMULATED_THREADS_USE_NATIVE_CALL_ONCE macro)
-  - Emulated `mtx_timelock()' with mtx_trylock() + *busy loop*
-*/
-
-struct impl_thrd_param {
-  thrd_start_t func;
-  void        *arg;
-  thrd_t       thrd;
-};
-
-struct thrd_state {
-  thrd_t thrd;
-  bool   handle_need_close;
-};
-
-static thread_local struct thrd_state impl_current_thread = { 0 };
-
-static unsigned __stdcall impl_thrd_routine(void *p) {
-  struct impl_thrd_param *pack_p = (struct impl_thrd_param *) p;
-  struct impl_thrd_param  pack;
-  int                     code;
-  impl_current_thread.thrd              = pack_p->thrd;
-  impl_current_thread.handle_need_close = false;
-  memcpy(&pack, pack_p, sizeof(struct impl_thrd_param));
-  free(p);
-  code = pack.func(pack.arg);
-  return (unsigned) code;
-}
-
-static time_t impl_timespec2msec(const struct timespec *ts) {
-  return (ts->tv_sec * 1000U) + (ts->tv_nsec / 1000000L);
-}
-
-static DWORD impl_abs2relmsec(const struct timespec *abs_time) {
-  const time_t    abs_ms = impl_timespec2msec(abs_time);
-  struct timespec now;
-  timespec_get(&now, TIME_UTC);
-  const time_t now_ms = impl_timespec2msec(&now);
-  const DWORD  rel_ms = (abs_ms > now_ms) ? (DWORD) (abs_ms - now_ms)
-                                          : 0;
-  return rel_ms;
-}
-
-#  ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
-struct impl_call_once_param {
-  void (*func)(void);
-};
-static BOOL CALLBACK impl_call_once_callback(PINIT_ONCE InitOnce,
-                                             PVOID      Parameter,
-                                             PVOID     *Context) {
-  struct impl_call_once_param *param = (struct impl_call_once_param *)
-      Parameter;
-  (param->func)();
-  ((void) InitOnce);
-  ((void) Context); // suppress warning
-  return TRUE;
-}
-#  endif // ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
-
-static struct impl_tss_dtor_entry {
-  tss_t      key;
-  tss_dtor_t dtor;
-} impl_tss_dtor_tbl[EMULATED_THREADS_TSS_DTOR_SLOTNUM];
-
-static int impl_tss_dtor_register(tss_t key, tss_dtor_t dtor) {
-  int i;
-  for (i = 0; i < EMULATED_THREADS_TSS_DTOR_SLOTNUM; i++) {
-    if (!impl_tss_dtor_tbl[i].dtor)
-      break;
-  }
-  if (i == EMULATED_THREADS_TSS_DTOR_SLOTNUM)
-    return 1;
-  impl_tss_dtor_tbl[i].key  = key;
-  impl_tss_dtor_tbl[i].dtor = dtor;
-  return 0;
-}
-
-static void impl_tss_dtor_invoke(void) {
-  int i;
-  for (i = 0; i < EMULATED_THREADS_TSS_DTOR_SLOTNUM; i++) {
-    if (impl_tss_dtor_tbl[i].dtor) {
-      void *val = tss_get(impl_tss_dtor_tbl[i].key);
-      if (val)
-        (impl_tss_dtor_tbl[i].dtor)(val);
-    }
-  }
-}
-
-/*--------------- 7.25.2 Initialization functions ---------------*/
-// 7.25.2.1
-void call_once(once_flag *flag, void (*func)(void)) {
-  assert(flag && func);
-#  ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
-  {
-    struct impl_call_once_param param;
-    param.func = func;
-    InitOnceExecuteOnce((PINIT_ONCE) flag, impl_call_once_callback,
-                        (PVOID) &param, NULL);
-  }
-#  else
-  if (InterlockedCompareExchangePointer(
-          (PVOID volatile *) &flag->status, (PVOID) 1, (PVOID) 0) ==
-      0) {
-    (func)();
-    InterlockedExchangePointer((PVOID volatile *) &flag->status,
-                               (PVOID) 2);
-  } else {
-    while (flag->status == 1) {
-      // busy loop!
-      thrd_yield();
-    }
-  }
-#  endif
-}
-
-/*------------- 7.25.3 Condition variable functions -------------*/
-// 7.25.3.1
-int cnd_broadcast(cnd_t *cond) {
-  assert(cond != NULL);
-  WakeAllConditionVariable((PCONDITION_VARIABLE) cond);
-  return thrd_success;
-}
-
-// 7.25.3.2
-void cnd_destroy(cnd_t *cond) {
-  (void) cond;
-  assert(cond != NULL);
-  // do nothing
-}
-
-// 7.25.3.3
-int cnd_init(cnd_t *cond) {
-  assert(cond != NULL);
-  InitializeConditionVariable((PCONDITION_VARIABLE) cond);
-  return thrd_success;
-}
-
-// 7.25.3.4
-int cnd_signal(cnd_t *cond) {
-  assert(cond != NULL);
-  WakeConditionVariable((PCONDITION_VARIABLE) cond);
-  return thrd_success;
-}
-
-// 7.25.3.5
-int cnd_timedwait(cnd_t *cond, mtx_t *mtx,
-                  const struct timespec *abs_time) {
-  assert(cond != NULL);
-  assert(mtx != NULL);
-  assert(abs_time != NULL);
-  const DWORD timeout = impl_abs2relmsec(abs_time);
-  if (SleepConditionVariableCS((PCONDITION_VARIABLE) cond,
-                               (PCRITICAL_SECTION) mtx, timeout))
-    return thrd_success;
-  return (GetLastError() == ERROR_TIMEOUT) ? thrd_timedout
-                                           : thrd_error;
-}
-
-// 7.25.3.6
-int cnd_wait(cnd_t *cond, mtx_t *mtx) {
-  assert(cond != NULL);
-  assert(mtx != NULL);
-  SleepConditionVariableCS((PCONDITION_VARIABLE) cond,
-                           (PCRITICAL_SECTION) mtx, INFINITE);
-  return thrd_success;
-}
-
-/*-------------------- 7.25.4 Mutex functions --------------------*/
-// 7.25.4.1
-void mtx_destroy(mtx_t *mtx) {
-  assert(mtx);
-  DeleteCriticalSection((PCRITICAL_SECTION) mtx);
-}
-
-// 7.25.4.2
-int mtx_init(mtx_t *mtx, int type) {
-  assert(mtx != NULL);
-  if (type != mtx_plain && type != mtx_timed &&
-      type != (mtx_plain | mtx_recursive) &&
-      type != (mtx_timed | mtx_recursive))
-    return thrd_error;
-  InitializeCriticalSection((PCRITICAL_SECTION) mtx);
-  return thrd_success;
-}
-
-// 7.25.4.3
-int mtx_lock(mtx_t *mtx) {
-  assert(mtx != NULL);
-  EnterCriticalSection((PCRITICAL_SECTION) mtx);
-  return thrd_success;
-}
-
-// 7.25.4.4
-int mtx_timedlock(mtx_t *mtx, const struct timespec *ts) {
-  assert(mtx != NULL);
-  assert(ts != NULL);
-  while (mtx_trylock(mtx) != thrd_success) {
-    if (impl_abs2relmsec(ts) == 0)
-      return thrd_timedout;
-    // busy loop!
-    thrd_yield();
-  }
-  return thrd_success;
-}
-
-// 7.25.4.5
-int mtx_trylock(mtx_t *mtx) {
-  assert(mtx != NULL);
-  return TryEnterCriticalSection((PCRITICAL_SECTION) mtx)
-             ? thrd_success
-             : thrd_busy;
-}
-
-// 7.25.4.6
-int mtx_unlock(mtx_t *mtx) {
-  assert(mtx != NULL);
-  LeaveCriticalSection((PCRITICAL_SECTION) mtx);
-  return thrd_success;
-}
-
-void __threads_win32_tls_callback(void) {
-  struct thrd_state *state = &impl_current_thread;
-  impl_tss_dtor_invoke();
-  if (state->handle_need_close) {
-    state->handle_need_close = false;
-    CloseHandle(state->thrd.handle);
-  }
-}
-
-/*------------------- 7.25.5 Thread functions -------------------*/
-// 7.25.5.1
-int thrd_create(thrd_t *thr, thrd_start_t func, void *arg) {
-  struct impl_thrd_param *pack;
-  uintptr_t               handle;
-  assert(thr != NULL);
-  pack = (struct impl_thrd_param *) malloc(
-      sizeof(struct impl_thrd_param));
-  if (!pack)
-    return thrd_nomem;
-  pack->func = func;
-  pack->arg  = arg;
-  handle     = _beginthreadex(NULL, 0, impl_thrd_routine, pack,
-                              CREATE_SUSPENDED, NULL);
-  if (handle == 0) {
-    free(pack);
-    if (errno == EAGAIN || errno == EACCES)
-      return thrd_nomem;
-    return thrd_error;
-  }
-  thr->handle = (void *) handle;
-  pack->thrd  = *thr;
-  ResumeThread((HANDLE) handle);
-  return thrd_success;
-}
-
-// 7.25.5.2
-thrd_t thrd_current(void) {
-  /* GetCurrentThread() returns a pseudo-handle, which we need
-   * to pass to DuplicateHandle(). Only the resulting handle can be
-   * used from other threads.
-   *
-   * Note that neither handle can be compared to the one by
-   * thread_create. Only the thread IDs - as returned by GetThreadId()
-   * and GetCurrentThreadId() can be compared directly.
-   *
-   * Other potential solutions would be:
-   * - define thrd_t as a thread Ids, but this would mean we'd need to
-   * OpenThread for many operations
-   * - use malloc'ed memory for thrd_t. This would imply using TLS for
-   * current thread.
-   *
-   * Neither is particularly nice.
-   *
-   * Life would be much easier if C11 threads had different
-   * abstractions for threads and thread IDs, just like C++11 threads
-   * does...
-   */
-  struct thrd_state *state = &impl_current_thread;
-  if (state->thrd.handle == NULL) {
-    if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
-                         GetCurrentProcess(), &(state->thrd.handle),
-                         0, FALSE, DUPLICATE_SAME_ACCESS)) {
-      abort();
-    }
-    state->handle_need_close = true;
-  }
-  return state->thrd;
-}
-
-// 7.25.5.3
-int thrd_detach(thrd_t thr) {
-  CloseHandle(thr.handle);
-  return thrd_success;
-}
-
-// 7.25.5.4
-int thrd_equal(thrd_t thr0, thrd_t thr1) {
-  return GetThreadId(thr0.handle) == GetThreadId(thr1.handle);
-}
-
-// 7.25.5.5
-_Noreturn void thrd_exit(int res) {
-  _endthreadex((unsigned) res);
-}
-
-// 7.25.5.6
-int thrd_join(thrd_t thr, int *res) {
-  DWORD w, code;
-  if (thr.handle == NULL) {
-    return thrd_error;
-  }
-  w = WaitForSingleObject(thr.handle, INFINITE);
-  if (w != WAIT_OBJECT_0)
-    return thrd_error;
-  if (res) {
-    if (!GetExitCodeThread(thr.handle, &code)) {
-      CloseHandle(thr.handle);
-      return thrd_error;
-    }
-    *res = (int) code;
-  }
-  CloseHandle(thr.handle);
-  return thrd_success;
-}
-
-// 7.25.5.7
-int thrd_sleep(const struct timespec *time_point,
-               struct timespec       *remaining) {
-  (void) remaining;
-  assert(time_point);
-  assert(!remaining); /* not implemented */
-  Sleep((DWORD) impl_timespec2msec(time_point));
-  return 0;
-}
-
-// 7.25.5.8
-void thrd_yield(void) {
-  SwitchToThread();
-}
-
-/*----------- 7.25.6 Thread-specific storage functions -----------*/
-// 7.25.6.1
-int tss_create(tss_t *key, tss_dtor_t dtor) {
-  assert(key != NULL);
-  *key = TlsAlloc();
-  if (dtor) {
-    if (impl_tss_dtor_register(*key, dtor)) {
-      TlsFree(*key);
-      return thrd_error;
-    }
-  }
-  return (*key != 0xFFFFFFFF) ? thrd_success : thrd_error;
-}
-
-// 7.25.6.2
-void tss_delete(tss_t key) {
-  TlsFree(key);
-}
-
-// 7.25.6.3
-void *tss_get(tss_t key) {
-  return TlsGetValue(key);
-}
-
-// 7.25.6.4
-int tss_set(tss_t key, void *val) {
-  return TlsSetValue(key, val) ? thrd_success : thrd_error;
-}
-
-#endif
diff --git a/source/kit/c11/impl/threads_win32.h b/source/kit/c11/impl/threads_win32.h
deleted file mode 100644
index 6adf5d2..0000000
--- a/source/kit/c11/impl/threads_win32.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright 2022 Yonggang Luo
- * SPDX-License-Identifier: MIT
- */
-
-#ifndef KIT_C11_IMPL_THREADS_WIN32_H_
-#define KIT_C11_IMPL_THREADS_WIN32_H_
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifdef KIT_HAVE_WINDOWS
-void __threads_win32_tls_callback(void);
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/source/kit/c11/impl/threads_win32_tls_callback.cpp b/source/kit/c11/impl/threads_win32_tls_callback.cpp
deleted file mode 100644
index 27874c0..0000000
--- a/source/kit/c11/impl/threads_win32_tls_callback.cpp
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * Copyright 2022 Yonggang Luo
- * SPDX-License-Identifier: MIT
- */
-
-#include "threads_win32.h"
-
-#ifdef KIT_HAVE_WINDOWS
-
-struct tls_callback {
-  tls_callback() { }
-  ~tls_callback() {
-    __threads_win32_tls_callback();
-  }
-};
-static thread_local tls_callback tls_callback_instance;
-
-#endif
diff --git a/source/kit/c11/impl/time.c b/source/kit/c11/impl/time.c
deleted file mode 100644
index 777b976..0000000
--- a/source/kit/c11/impl/time.c
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * C11 <time.h> implementation
- *
- * (C) Copyright yohhoy 2012.
- * Copyright 2022 Yonggang Luo
- * Distributed under the Boost Software License, Version 1.0.
- *
- * Permission is hereby granted, free of charge, to any person or
- * organization obtaining a copy of the software and accompanying
- * documentation covered by this license (the "Software") to use,
- * reproduce, display, distribute, execute, and transmit the Software,
- * and to prepare [[derivative work]]s of the Software, and to permit
- * third-parties to whom the Software is furnished to do so, all
- * subject to the following:
- *
- * The copyright notices in the Software and this entire statement,
- * including the above license grant, this restriction and the
- * following disclaimer, must be included in all copies of the
- * Software, in whole or in part, and all derivative works of the
- * Software, unless such copies or derivative works are solely in the
- * form of machine-executable object code generated by a source
- * language processor.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
- * NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE
- * DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER
- * LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-
-#include "time.h"
-
-#ifndef KIT_HAVE_TIMESPEC_GET
-
-#  if defined(_WIN32) && !defined(__CYGWIN__)
-
-#    ifndef WIN32_LEAN_AND_MEAN
-#      define WIN32_LEAN_AND_MEAN 1
-#    endif
-#    include <windows.h>
-
-int timespec_get(struct timespec *ts, int base) {
-/* difference between 1970 and 1601 */
-#    define _TIMESPEC_IMPL_UNIX_EPOCH_IN_TICKS 116444736000000000ull
-/* 1 tick is 100 nanoseconds */
-#    define _TIMESPEC_IMPL_TICKS_PER_SECONDS 10000000ull
-  if (!ts)
-    return 0;
-  if (base == TIME_UTC) {
-    FILETIME       ft;
-    ULARGE_INTEGER date;
-    LONGLONG       ticks;
-
-    GetSystemTimeAsFileTime(&ft);
-    date.HighPart = ft.dwHighDateTime;
-    date.LowPart  = ft.dwLowDateTime;
-    ticks         = (LONGLONG) (date.QuadPart -
-                        _TIMESPEC_IMPL_UNIX_EPOCH_IN_TICKS);
-    ts->tv_sec    = ticks / _TIMESPEC_IMPL_TICKS_PER_SECONDS;
-    ts->tv_nsec   = (ticks % _TIMESPEC_IMPL_TICKS_PER_SECONDS) * 100;
-    return base;
-  }
-  return 0;
-#    undef _TIMESPEC_IMPL_UNIX_EPOCH_IN_TICKS
-#    undef _TIMESPEC_IMPL_TICKS_PER_SECONDS
-}
-
-#  else
-
-int timespec_get(struct timespec *ts, int base) {
-  if (!ts)
-    return 0;
-  if (base == TIME_UTC) {
-    clock_gettime(CLOCK_REALTIME, ts);
-    return base;
-  }
-  return 0;
-}
-#  endif
-
-#endif /* KIT_HAVE_TIMESPEC_GET */
diff --git a/source/kit/c11/threads.h b/source/kit/c11/threads.h
deleted file mode 100644
index 0b73183..0000000
--- a/source/kit/c11/threads.h
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * C11 <threads.h> emulation library
- *
- * (C) Copyright yohhoy 2012.
- * Copyright 2022 Yonggang Luo
- * Distributed under the Boost Software License, Version 1.0.
- *
- * Permission is hereby granted, free of charge, to any person or
- * organization obtaining a copy of the software and accompanying
- * documentation covered by this license (the "Software") to use,
- * reproduce, display, distribute, execute, and transmit the Software,
- * and to prepare [[derivative work]]s of the Software, and to permit
- * third-parties to whom the Software is furnished to do so, all
- * subject to the following:
- *
- * The copyright notices in the Software and this entire statement,
- * including the above license grant, this restriction and the
- * following disclaimer, must be included in all copies of the
- * Software, in whole or in part, and all derivative works of the
- * Software, unless such copies or derivative works are solely in the
- * form of machine-executable object code generated by a source
- * language processor.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
- * NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE
- * DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER
- * LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-
-#ifndef KIT_C11_THREADS_H_INCLUDED_
-#define KIT_C11_THREADS_H_INCLUDED_
-
-#include "time.h"
-
-#include <errno.h>
-#include <limits.h>
-#include <stdlib.h>
-
-#ifdef _MSC_VER
-#  define _Noreturn __declspec(noreturn)
-#endif
-
-#if defined(_WIN32) && !defined(__CYGWIN__)
-#  include <io.h>      /* close */
-#  include <process.h> /* _exit */
-#elif defined(KIT_HAVE_PTHREAD)
-#  include <pthread.h>
-#  include <unistd.h> /* close, _exit */
-#else
-#  error Not supported on this platform.
-#endif
-
-/*---------------------------- macros ---------------------------*/
-
-#ifndef _Thread_local
-#  if defined(__cplusplus)
-/* C++11 doesn't need `_Thread_local` keyword or macro */
-#  elif !defined(__STDC_NO_THREADS__)
-/* threads are optional in C11, _Thread_local present in this
- * condition */
-#  elif defined(_MSC_VER)
-#    define _Thread_local __declspec(thread)
-#  elif defined(__GNUC__)
-#    define _Thread_local __thread
-#  else
-/* Leave _Thread_local undefined so that use of _Thread_local would
- * not promote to a non-thread-local global variable
- */
-#  endif
-#endif
-
-#if !defined(__cplusplus)
-/*
- * C11 thread_local() macro
- * C++11 and above already have thread_local keyword
- */
-#  ifndef thread_local
-#    if _MSC_VER
-#      define thread_local __declspec(thread)
-#    else
-#      define thread_local _Thread_local
-#    endif
-#  endif
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*---------------------------- types ----------------------------*/
-typedef void (*tss_dtor_t)(void *);
-typedef int (*thrd_start_t)(void *);
-
-#if defined(_WIN32) && !defined(__CYGWIN__)
-typedef struct {
-  void *Ptr;
-} cnd_t;
-/* Define thrd_t as struct type intentionally for avoid use of thrd_t
- * as pointer type */
-typedef struct {
-  void *handle;
-} thrd_t;
-typedef unsigned long tss_t;
-typedef struct {
-  void     *DebugInfo;
-  long      LockCount;
-  long      RecursionCount;
-  void     *OwningThread;
-  void     *LockSemaphore;
-  uintptr_t SpinCount;
-} mtx_t; /* Mock of CRITICAL_SECTION */
-typedef struct {
-  volatile uintptr_t status;
-} once_flag;
-// FIXME: temporary non-standard hack to ease transition
-#  define KIT_MTX_INITIALIZER_NP_ \
-    { (void *) -1, -1, 0, 0, 0, 0 }
-#  define ONCE_FLAG_INIT \
-    { 0 }
-#  define TSS_DTOR_ITERATIONS 1
-#elif defined(KIT_HAVE_PTHREAD)
-typedef pthread_cond_t  cnd_t;
-typedef pthread_t       thrd_t;
-typedef pthread_key_t   tss_t;
-typedef pthread_mutex_t mtx_t;
-typedef pthread_once_t  once_flag;
-// FIXME: temporary non-standard hack to ease transition
-#  define KIT_MTX_INITIALIZER_NP_ PTHREAD_MUTEX_INITIALIZER
-#  define ONCE_FLAG_INIT PTHREAD_ONCE_INIT
-#  ifdef PTHREAD_DESTRUCTOR_ITERATIONS
-#    define TSS_DTOR_ITERATIONS PTHREAD_DESTRUCTOR_ITERATIONS
-#  else
-#    define TSS_DTOR_ITERATIONS \
-      1 // assume TSS dtor MAY be called at least once.
-#  endif
-#else
-#  error Not supported on this platform.
-#endif
-
-/*-------------------- enumeration constants --------------------*/
-enum {
-  mtx_plain     = 0,
-  mtx_recursive = 1,
-  mtx_timed     = 2,
-};
-
-enum {
-  thrd_success = 0, // succeeded
-  thrd_timedout,    // timed out
-  thrd_error,       // failed
-  thrd_busy,        // resource busy
-  thrd_nomem        // out of memory
-};
-
-/*-------------------------- functions --------------------------*/
-
-void   call_once(once_flag *, void (*)(void));
-int    cnd_broadcast(cnd_t *);
-void   cnd_destroy(cnd_t *);
-int    cnd_init(cnd_t *);
-int    cnd_signal(cnd_t *);
-int    cnd_timedwait(cnd_t *__restrict, mtx_t *__restrict mtx_,
-                     const struct timespec *__restrict);
-int    cnd_wait(cnd_t *, mtx_t *mtx_);
-void   mtx_destroy(mtx_t *mtx_);
-int    mtx_init(mtx_t *mtx_, int);
-int    mtx_lock(mtx_t *mtx_);
-int    mtx_timedlock(mtx_t *__restrict mtx_,
-                     const struct timespec *__restrict);
-int    mtx_trylock(mtx_t *mtx_);
-int    mtx_unlock(mtx_t *mtx_);
-int    thrd_create(thrd_t *, thrd_start_t, void *);
-thrd_t thrd_current(void);
-int    thrd_detach(thrd_t);
-int    thrd_equal(thrd_t, thrd_t);
-#if defined(__cplusplus)
-[[ noreturn ]]
-#else
-_Noreturn
-#endif
-void thrd_exit(int);
-int   thrd_join(thrd_t, int *);
-int   thrd_sleep(const struct timespec *, struct timespec *);
-void  thrd_yield(void);
-int   tss_create(tss_t *, tss_dtor_t);
-void  tss_delete(tss_t);
-void *tss_get(tss_t);
-int   tss_set(tss_t, void *);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* KIT_C11_THREADS_H_INCLUDED_ */
diff --git a/source/kit/c11/time.h b/source/kit/c11/time.h
deleted file mode 100644
index 7467a33..0000000
--- a/source/kit/c11/time.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright 2022 Yonggang Luo
- * SPDX-License-Identifier: MIT
- *
- * C11 <time.h> emulation library
- */
-
-#ifndef KIT_C11_TIME_H_INCLUDED_
-#define KIT_C11_TIME_H_INCLUDED_
-
-#include <time.h>
-
-/*---------------------------- macros ---------------------------*/
-
-#ifndef TIME_UTC
-#  define TIME_UTC 1
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*---------------------------- types ----------------------------*/
-
-/*
- * On MINGW `struct timespec` present but `timespec_get` may not
- * present; On MSVC `struct timespec` and `timespec_get` present at
- * the same time; So detecting `HAVE_STRUCT_TIMESPEC` in meson script
- * dynamically.
- */
-#ifndef KIT_HAVE_STRUCT_TIMESPEC
-struct timespec {
-  time_t tv_sec;  // Seconds - >= 0
-  long   tv_nsec; // Nanoseconds - [0, 999999999]
-};
-#endif
-
-/*-------------------------- functions --------------------------*/
-
-#if !defined(KIT_HAVE_TIMESPEC_GET)
-#  define KIT_HAVE_TIMESPEC_GET_NEED_DECL_
-#elif defined(__APPLE__) && defined(__cplusplus) && \
-    (__cplusplus < 201703L)
-/* On macOS, the guard for declaration of timespec_get is by
- * (defined(__cplusplus) && __cplusplus >= 201703L),
- * fix the declaration for C++14 and lower here
- */
-#  define KIT_HAVE_TIMESPEC_GET_NEED_DECL_
-#endif
-
-#ifdef KIT_HAVE_TIMESPEC_GET_NEED_DECL_
-/*-------------------- 7.25.7 Time functions --------------------*/
-// 7.25.6.1
-int timespec_get(struct timespec *ts, int base);
-#  undef KIT_HAVE_TIMESPEC_GET_NEED_DECL_
-#endif
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* KIT_C11_TIME_H_INCLUDED_ */
diff --git a/source/kit/mersenne_twister_64.c b/source/kit/mersenne_twister_64.c
index 4be4313..9ab39d3 100644
--- a/source/kit/mersenne_twister_64.c
+++ b/source/kit/mersenne_twister_64.c
@@ -1,6 +1,6 @@
 #include "mersenne_twister_64.h"
 
-#include <time.h>
+#include "time.h"
 
 void kit_mt64_init(kit_mt64_state_t *state, uint64_t seed) {
   state->mt[0] = seed;
diff --git a/source/kit/threads.h b/source/kit/threads.h
new file mode 100644
index 0000000..d98978d
--- /dev/null
+++ b/source/kit/threads.h
@@ -0,0 +1,201 @@
+/*
+ * C11 <threads.h> emulation library
+ *
+ * (C) Copyright yohhoy 2012.
+ * Copyright 2022 Yonggang Luo
+ * Distributed under the Boost Software License, Version 1.0.
+ *
+ * Permission is hereby granted, free of charge, to any person or
+ * organization obtaining a copy of the software and accompanying
+ * documentation covered by this license (the "Software") to use,
+ * reproduce, display, distribute, execute, and transmit the Software,
+ * and to prepare [[derivative work]]s of the Software, and to permit
+ * third-parties to whom the Software is furnished to do so, all
+ * subject to the following:
+ *
+ * The copyright notices in the Software and this entire statement,
+ * including the above license grant, this restriction and the
+ * following disclaimer, must be included in all copies of the
+ * Software, in whole or in part, and all derivative works of the
+ * Software, unless such copies or derivative works are solely in the
+ * form of machine-executable object code generated by a source
+ * language processor.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
+ * NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE
+ * DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef KIT_THREADS_H
+#define KIT_THREADS_H
+
+#ifndef KIT_DISABLE_SYSTEM_THREADS
+#  include "time.h"
+
+#  include <errno.h>
+#  include <limits.h>
+#  include <stdlib.h>
+
+#  ifdef _MSC_VER
+#    define _Noreturn __declspec(noreturn)
+#  endif
+
+#  if defined(_WIN32) && !defined(__CYGWIN__)
+#    include <io.h>      /* close */
+#    include <process.h> /* _exit */
+#  elif defined(KIT_HAVE_PTHREAD)
+#    include <pthread.h>
+#    include <unistd.h> /* close, _exit */
+#  else
+#    error Not supported on this platform.
+#  endif
+
+/*---------------------------- macros ---------------------------*/
+
+#  ifndef _Thread_local
+#    if defined(__cplusplus)
+/* C++11 doesn't need `_Thread_local` keyword or macro */
+#    elif !defined(__STDC_NO_THREADS__)
+/* threads are optional in C11, _Thread_local present in this
+ * condition */
+#    elif defined(_MSC_VER)
+#      define _Thread_local __declspec(thread)
+#    elif defined(__GNUC__)
+#      define _Thread_local __thread
+#    else
+/* Leave _Thread_local undefined so that use of _Thread_local would
+ * not promote to a non-thread-local global variable
+ */
+#    endif
+#  endif
+
+#  if !defined(__cplusplus)
+/*
+ * C11 thread_local() macro
+ * C++11 and above already have thread_local keyword
+ */
+#    ifndef thread_local
+#      if _MSC_VER
+#        define thread_local __declspec(thread)
+#      else
+#        define thread_local _Thread_local
+#      endif
+#    endif
+#  endif
+
+#  ifdef __cplusplus
+extern "C" {
+#  endif
+
+/*---------------------------- types ----------------------------*/
+typedef void (*tss_dtor_t)(void *);
+typedef int (*thrd_start_t)(void *);
+
+#  if defined(_WIN32) && !defined(__CYGWIN__)
+typedef struct {
+  void *Ptr;
+} cnd_t;
+/* Define thrd_t as struct type intentionally for avoid use of thrd_t
+ * as pointer type */
+typedef struct {
+  void *handle;
+} thrd_t;
+typedef unsigned long tss_t;
+typedef struct {
+  void     *DebugInfo;
+  long      LockCount;
+  long      RecursionCount;
+  void     *OwningThread;
+  void     *LockSemaphore;
+  uintptr_t SpinCount;
+} mtx_t; /* Mock of CRITICAL_SECTION */
+typedef struct {
+  volatile uintptr_t status;
+} once_flag;
+// FIXME: temporary non-standard hack to ease transition
+#    define KIT_MTX_INITIALIZER_NP_ \
+      { (void *) -1, -1, 0, 0, 0, 0 }
+#    define ONCE_FLAG_INIT \
+      { 0 }
+#    define TSS_DTOR_ITERATIONS 1
+#  elif defined(KIT_HAVE_PTHREAD)
+typedef pthread_cond_t  cnd_t;
+typedef pthread_t       thrd_t;
+typedef pthread_key_t   tss_t;
+typedef pthread_mutex_t mtx_t;
+typedef pthread_once_t  once_flag;
+// FIXME: temporary non-standard hack to ease transition
+#    define KIT_MTX_INITIALIZER_NP_ PTHREAD_MUTEX_INITIALIZER
+#    define ONCE_FLAG_INIT PTHREAD_ONCE_INIT
+#    ifdef PTHREAD_DESTRUCTOR_ITERATIONS
+#      define TSS_DTOR_ITERATIONS PTHREAD_DESTRUCTOR_ITERATIONS
+#    else
+#      define TSS_DTOR_ITERATIONS \
+        1 // assume TSS dtor MAY be called at least once.
+#    endif
+#  else
+#    error Not supported on this platform.
+#  endif
+
+/*-------------------- enumeration constants --------------------*/
+enum {
+  mtx_plain     = 0,
+  mtx_recursive = 1,
+  mtx_timed     = 2,
+};
+
+enum {
+  thrd_success = 0, // succeeded
+  thrd_timedout,    // timed out
+  thrd_error,       // failed
+  thrd_busy,        // resource busy
+  thrd_nomem        // out of memory
+};
+
+/*-------------------------- functions --------------------------*/
+
+void   call_once(once_flag *, void (*)(void));
+int    cnd_broadcast(cnd_t *);
+void   cnd_destroy(cnd_t *);
+int    cnd_init(cnd_t *);
+int    cnd_signal(cnd_t *);
+int    cnd_timedwait(cnd_t *__restrict, mtx_t *__restrict mtx_,
+                     const struct timespec *__restrict);
+int    cnd_wait(cnd_t *, mtx_t *mtx_);
+void   mtx_destroy(mtx_t *mtx_);
+int    mtx_init(mtx_t *mtx_, int);
+int    mtx_lock(mtx_t *mtx_);
+int    mtx_timedlock(mtx_t *__restrict mtx_,
+                     const struct timespec *__restrict);
+int    mtx_trylock(mtx_t *mtx_);
+int    mtx_unlock(mtx_t *mtx_);
+int    thrd_create(thrd_t *, thrd_start_t, void *);
+thrd_t thrd_current(void);
+int    thrd_detach(thrd_t);
+int    thrd_equal(thrd_t, thrd_t);
+#  if defined(__cplusplus)
+[[ noreturn ]]
+#  else
+_Noreturn
+#  endif
+void thrd_exit(int);
+int   thrd_join(thrd_t, int *);
+int   thrd_sleep(const struct timespec *, struct timespec *);
+void  thrd_yield(void);
+int   tss_create(tss_t *, tss_dtor_t);
+void  tss_delete(tss_t);
+void *tss_get(tss_t);
+int   tss_set(tss_t, void *);
+
+#  ifdef __cplusplus
+}
+#  endif
+
+#endif /* KIT_DISABLE_SYSTEM_THREADS */
+
+#endif /* KIT_THREADS_H */
diff --git a/source/kit/threads.posix.c b/source/kit/threads.posix.c
new file mode 100644
index 0000000..20ef1ae
--- /dev/null
+++ b/source/kit/threads.posix.c
@@ -0,0 +1,315 @@
+/*
+ * C11 <threads.h> emulation library
+ *
+ * (C) Copyright yohhoy 2012.
+ * Distributed under the Boost Software License, Version 1.0.
+ *
+ * Permission is hereby granted, free of charge, to any person or
+ * organization obtaining a copy of the software and accompanying
+ * documentation covered by this license (the "Software") to use,
+ * reproduce, display, distribute, execute, and transmit the Software,
+ * and to prepare [[derivative work]]s of the Software, and to permit
+ * third-parties to whom the Software is furnished to do so, all
+ * subject to the following:
+ *
+ * The copyright notices in the Software and this entire statement,
+ * including the above license grant, this restriction and the
+ * following disclaimer, must be included in all copies of the
+ * Software, in whole or in part, and all derivative works of the
+ * Software, unless such copies or derivative works are solely in the
+ * form of machine-executable object code generated by a source
+ * language processor.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
+ * NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE
+ * DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+#ifndef KIT_DISABLE_SYSTEM_THREADS
+#  ifdef KIT_HAVE_PTHREAD
+
+#    include <assert.h>
+#    include <errno.h>
+#    include <limits.h>
+#    include <sched.h>
+#    include <stdint.h> /* for intptr_t */
+#    include <stdlib.h>
+#    include <unistd.h>
+
+#    include "threads.h"
+
+/*
+Configuration macro:
+
+  EMULATED_THREADS_USE_NATIVE_TIMEDLOCK
+    Use pthread_mutex_timedlock() for `mtx_timedlock()'
+    Otherwise use mtx_trylock() + *busy loop* emulation.
+*/
+#    if !defined(__CYGWIN__) && !defined(__APPLE__) && \
+        !defined(__NetBSD__)
+#      define EMULATED_THREADS_USE_NATIVE_TIMEDLOCK
+#    endif
+
+/*---------------------------- types ----------------------------*/
+
+/*
+Implementation limits:
+  - Conditionally emulation for "mutex with timeout"
+    (see EMULATED_THREADS_USE_NATIVE_TIMEDLOCK macro)
+*/
+struct impl_thrd_param {
+  thrd_start_t func;
+  void        *arg;
+};
+
+static void *impl_thrd_routine(void *p) {
+  struct impl_thrd_param pack = *((struct impl_thrd_param *) p);
+  free(p);
+  return (void *) (intptr_t) pack.func(pack.arg);
+}
+
+/*--------------- 7.25.2 Initialization functions ---------------*/
+// 7.25.2.1
+void call_once(once_flag *flag, void (*func)(void)) {
+  pthread_once(flag, func);
+}
+
+/*------------- 7.25.3 Condition variable functions -------------*/
+// 7.25.3.1
+int cnd_broadcast(cnd_t *cond) {
+  assert(cond != NULL);
+  return (pthread_cond_broadcast(cond) == 0) ? thrd_success
+                                             : thrd_error;
+}
+
+// 7.25.3.2
+void cnd_destroy(cnd_t *cond) {
+  assert(cond);
+  pthread_cond_destroy(cond);
+}
+
+// 7.25.3.3
+int cnd_init(cnd_t *cond) {
+  assert(cond != NULL);
+  return (pthread_cond_init(cond, NULL) == 0) ? thrd_success
+                                              : thrd_error;
+}
+
+// 7.25.3.4
+int cnd_signal(cnd_t *cond) {
+  assert(cond != NULL);
+  return (pthread_cond_signal(cond) == 0) ? thrd_success : thrd_error;
+}
+
+// 7.25.3.5
+int cnd_timedwait(cnd_t *cond, mtx_t *mtx,
+                  const struct timespec *abs_time) {
+  int rt;
+
+  assert(mtx != NULL);
+  assert(cond != NULL);
+  assert(abs_time != NULL);
+
+  rt = pthread_cond_timedwait(cond, mtx, abs_time);
+  if (rt == ETIMEDOUT)
+    return thrd_timedout;
+  return (rt == 0) ? thrd_success : thrd_error;
+}
+
+// 7.25.3.6
+int cnd_wait(cnd_t *cond, mtx_t *mtx) {
+  assert(mtx != NULL);
+  assert(cond != NULL);
+  return (pthread_cond_wait(cond, mtx) == 0) ? thrd_success
+                                             : thrd_error;
+}
+
+/*-------------------- 7.25.4 Mutex functions --------------------*/
+// 7.25.4.1
+void mtx_destroy(mtx_t *mtx) {
+  assert(mtx != NULL);
+  pthread_mutex_destroy(mtx);
+}
+
+/*
+ * XXX: Workaround when building with -O0 and without pthreads link.
+ *
+ * In such cases constant folding and dead code elimination won't be
+ * available, thus the compiler will always add the pthread_mutexattr*
+ * functions into the binary. As we try to link, we'll fail as the
+ * symbols are unresolved.
+ *
+ * Ideally we'll enable the optimisations locally, yet that does not
+ * seem to work.
+ *
+ * So the alternative workaround is to annotate the symbols as weak.
+ * Thus the linker will be happy and things don't clash when building
+ * with -O1 or greater.
+ */
+#    if defined(HAVE_FUNC_ATTRIBUTE_WEAK) && !defined(__CYGWIN__)
+__attribute__((weak)) int pthread_mutexattr_init(
+    pthread_mutexattr_t *attr);
+
+__attribute__((weak)) int pthread_mutexattr_settype(
+    pthread_mutexattr_t *attr, int type);
+
+__attribute__((weak)) int pthread_mutexattr_destroy(
+    pthread_mutexattr_t *attr);
+#    endif
+
+// 7.25.4.2
+int mtx_init(mtx_t *mtx, int type) {
+  pthread_mutexattr_t attr;
+  assert(mtx != NULL);
+  if (type != mtx_plain && type != mtx_timed &&
+      type != (mtx_plain | mtx_recursive) &&
+      type != (mtx_timed | mtx_recursive))
+    return thrd_error;
+
+  if ((type & mtx_recursive) == 0) {
+    pthread_mutex_init(mtx, NULL);
+    return thrd_success;
+  }
+
+  pthread_mutexattr_init(&attr);
+  pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+  pthread_mutex_init(mtx, &attr);
+  pthread_mutexattr_destroy(&attr);
+  return thrd_success;
+}
+
+// 7.25.4.3
+int mtx_lock(mtx_t *mtx) {
+  assert(mtx != NULL);
+  return (pthread_mutex_lock(mtx) == 0) ? thrd_success : thrd_error;
+}
+
+// 7.25.4.4
+int mtx_timedlock(mtx_t *mtx, const struct timespec *ts) {
+  assert(mtx != NULL);
+  assert(ts != NULL);
+
+  {
+#    ifdef EMULATED_THREADS_USE_NATIVE_TIMEDLOCK
+    int rt;
+    rt = pthread_mutex_timedlock(mtx, ts);
+    if (rt == 0)
+      return thrd_success;
+    return (rt == ETIMEDOUT) ? thrd_timedout : thrd_error;
+#    else
+    time_t expire = time(NULL);
+    expire += ts->tv_sec;
+    while (mtx_trylock(mtx) != thrd_success) {
+      time_t now = time(NULL);
+      if (expire < now)
+        return thrd_timedout;
+      // busy loop!
+      thrd_yield();
+    }
+    return thrd_success;
+#    endif
+  }
+}
+
+// 7.25.4.5
+int mtx_trylock(mtx_t *mtx) {
+  assert(mtx != NULL);
+  return (pthread_mutex_trylock(mtx) == 0) ? thrd_success : thrd_busy;
+}
+
+// 7.25.4.6
+int mtx_unlock(mtx_t *mtx) {
+  assert(mtx != NULL);
+  return (pthread_mutex_unlock(mtx) == 0) ? thrd_success : thrd_error;
+}
+
+/*------------------- 7.25.5 Thread functions -------------------*/
+// 7.25.5.1
+int thrd_create(thrd_t *thr, thrd_start_t func, void *arg) {
+  struct impl_thrd_param *pack;
+  assert(thr != NULL);
+  pack = (struct impl_thrd_param *) malloc(
+      sizeof(struct impl_thrd_param));
+  if (!pack)
+    return thrd_nomem;
+  pack->func = func;
+  pack->arg  = arg;
+  if (pthread_create(thr, NULL, impl_thrd_routine, pack) != 0) {
+    free(pack);
+    return thrd_error;
+  }
+  return thrd_success;
+}
+
+// 7.25.5.2
+thrd_t thrd_current(void) {
+  return pthread_self();
+}
+
+// 7.25.5.3
+int thrd_detach(thrd_t thr) {
+  return (pthread_detach(thr) == 0) ? thrd_success : thrd_error;
+}
+
+// 7.25.5.4
+int thrd_equal(thrd_t thr0, thrd_t thr1) {
+  return pthread_equal(thr0, thr1);
+}
+
+// 7.25.5.5
+_Noreturn void thrd_exit(int res) {
+  pthread_exit((void *) (intptr_t) res);
+}
+
+// 7.25.5.6
+int thrd_join(thrd_t thr, int *res) {
+  void *code;
+  if (pthread_join(thr, &code) != 0)
+    return thrd_error;
+  if (res)
+    *res = (int) (intptr_t) code;
+  return thrd_success;
+}
+
+// 7.25.5.7
+int thrd_sleep(const struct timespec *time_point,
+               struct timespec       *remaining) {
+  assert(time_point != NULL);
+  return nanosleep(time_point, remaining);
+}
+
+// 7.25.5.8
+void thrd_yield(void) {
+  sched_yield();
+}
+
+/*----------- 7.25.6 Thread-specific storage functions -----------*/
+// 7.25.6.1
+int tss_create(tss_t *key, tss_dtor_t dtor) {
+  assert(key != NULL);
+  return (pthread_key_create(key, dtor) == 0) ? thrd_success
+                                              : thrd_error;
+}
+
+// 7.25.6.2
+void tss_delete(tss_t key) {
+  pthread_key_delete(key);
+}
+
+// 7.25.6.3
+void *tss_get(tss_t key) {
+  return pthread_getspecific(key);
+}
+
+// 7.25.6.4
+int tss_set(tss_t key, void *val) {
+  return (pthread_setspecific(key, val) == 0) ? thrd_success
+                                              : thrd_error;
+}
+
+#  endif
+#endif
diff --git a/source/kit/threads.win32.c b/source/kit/threads.win32.c
new file mode 100644
index 0000000..d425a27
--- /dev/null
+++ b/source/kit/threads.win32.c
@@ -0,0 +1,462 @@
+/*
+ * C11 <threads.h> emulation library
+ *
+ * (C) Copyright yohhoy 2012.
+ * Distributed under the Boost Software License, Version 1.0.
+ *
+ * Permission is hereby granted, free of charge, to any person or
+ * organization obtaining a copy of the software and accompanying
+ * documentation covered by this license (the "Software") to use,
+ * reproduce, display, distribute, execute, and transmit the Software,
+ * and to prepare [[derivative work]]s of the Software, and to permit
+ * third-parties to whom the Software is furnished to do so, all
+ * subject to the following:
+ *
+ * The copyright notices in the Software and this entire statement,
+ * including the above license grant, this restriction and the
+ * following disclaimer, must be included in all copies of the
+ * Software, in whole or in part, and all derivative works of the
+ * Software, unless such copies or derivative works are solely in the
+ * form of machine-executable object code generated by a source
+ * language processor.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
+ * NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE
+ * DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+#ifndef KIT_DISABLE_SYSTEM_THREADS
+#  if defined(_WIN32) && !defined(__CYGWIN__)
+
+#    include <assert.h>
+#    include <errno.h>
+#    include <limits.h>
+#    include <process.h> // MSVCRT
+#    include <stdbool.h>
+#    include <stdlib.h>
+
+#    include "threads.h"
+
+#    include "threads_win32.h"
+
+#    ifndef WIN32_LEAN_AND_MEAN
+#      define WIN32_LEAN_AND_MEAN 1
+#    endif
+#    include <windows.h>
+
+/*
+Configuration macro:
+
+  EMULATED_THREADS_USE_NATIVE_CALL_ONCE
+    Use native WindowsAPI one-time initialization function.
+    (requires WinVista or later)
+    Otherwise emulate by mtx_trylock() + *busy loop* for WinXP.
+
+  EMULATED_THREADS_TSS_DTOR_SLOTNUM
+    Max registerable TSS dtor number.
+*/
+
+#    if _WIN32_WINNT >= 0x0600
+// Prefer native WindowsAPI on newer environment.
+#      if !defined(__MINGW32__)
+#        define EMULATED_THREADS_USE_NATIVE_CALL_ONCE
+#      endif
+#    endif
+#    define EMULATED_THREADS_TSS_DTOR_SLOTNUM \
+      64 // see TLS_MINIMUM_AVAILABLE
+
+// check configuration
+#    if defined(EMULATED_THREADS_USE_NATIVE_CALL_ONCE) && \
+        (_WIN32_WINNT < 0x0600)
+#      error EMULATED_THREADS_USE_NATIVE_CALL_ONCE requires _WIN32_WINNT>=0x0600
+#    endif
+
+static_assert(sizeof(cnd_t) == sizeof(CONDITION_VARIABLE),
+              "The size of cnd_t must equal to CONDITION_VARIABLE");
+static_assert(sizeof(thrd_t) == sizeof(HANDLE),
+              "The size of thrd_t must equal to HANDLE");
+static_assert(sizeof(tss_t) == sizeof(DWORD),
+              "The size of tss_t must equal to DWORD");
+static_assert(sizeof(mtx_t) == sizeof(CRITICAL_SECTION),
+              "The size of mtx_t must equal to CRITICAL_SECTION");
+static_assert(sizeof(once_flag) == sizeof(INIT_ONCE),
+              "The size of once_flag must equal to INIT_ONCE");
+
+/*
+Implementation limits:
+  - Conditionally emulation for "Initialization functions"
+    (see EMULATED_THREADS_USE_NATIVE_CALL_ONCE macro)
+  - Emulated `mtx_timelock()' with mtx_trylock() + *busy loop*
+*/
+
+struct impl_thrd_param {
+  thrd_start_t func;
+  void        *arg;
+  thrd_t       thrd;
+};
+
+struct thrd_state {
+  thrd_t thrd;
+  bool   handle_need_close;
+};
+
+static thread_local struct thrd_state impl_current_thread = { 0 };
+
+static unsigned __stdcall impl_thrd_routine(void *p) {
+  struct impl_thrd_param *pack_p = (struct impl_thrd_param *) p;
+  struct impl_thrd_param  pack;
+  int                     code;
+  impl_current_thread.thrd              = pack_p->thrd;
+  impl_current_thread.handle_need_close = false;
+  memcpy(&pack, pack_p, sizeof(struct impl_thrd_param));
+  free(p);
+  code = pack.func(pack.arg);
+  return (unsigned) code;
+}
+
+static time_t impl_timespec2msec(const struct timespec *ts) {
+  return (ts->tv_sec * 1000U) + (ts->tv_nsec / 1000000L);
+}
+
+static DWORD impl_abs2relmsec(const struct timespec *abs_time) {
+  const time_t    abs_ms = impl_timespec2msec(abs_time);
+  struct timespec now;
+  timespec_get(&now, TIME_UTC);
+  const time_t now_ms = impl_timespec2msec(&now);
+  const DWORD  rel_ms = (abs_ms > now_ms) ? (DWORD) (abs_ms - now_ms)
+                                          : 0;
+  return rel_ms;
+}
+
+#    ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
+struct impl_call_once_param {
+  void (*func)(void);
+};
+static BOOL CALLBACK impl_call_once_callback(PINIT_ONCE InitOnce,
+                                             PVOID      Parameter,
+                                             PVOID     *Context) {
+  struct impl_call_once_param *param = (struct impl_call_once_param *)
+      Parameter;
+  (param->func)();
+  ((void) InitOnce);
+  ((void) Context); // suppress warning
+  return TRUE;
+}
+#    endif // ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
+
+static struct impl_tss_dtor_entry {
+  tss_t      key;
+  tss_dtor_t dtor;
+} impl_tss_dtor_tbl[EMULATED_THREADS_TSS_DTOR_SLOTNUM];
+
+static int impl_tss_dtor_register(tss_t key, tss_dtor_t dtor) {
+  int i;
+  for (i = 0; i < EMULATED_THREADS_TSS_DTOR_SLOTNUM; i++) {
+    if (!impl_tss_dtor_tbl[i].dtor)
+      break;
+  }
+  if (i == EMULATED_THREADS_TSS_DTOR_SLOTNUM)
+    return 1;
+  impl_tss_dtor_tbl[i].key  = key;
+  impl_tss_dtor_tbl[i].dtor = dtor;
+  return 0;
+}
+
+static void impl_tss_dtor_invoke(void) {
+  int i;
+  for (i = 0; i < EMULATED_THREADS_TSS_DTOR_SLOTNUM; i++) {
+    if (impl_tss_dtor_tbl[i].dtor) {
+      void *val = tss_get(impl_tss_dtor_tbl[i].key);
+      if (val)
+        (impl_tss_dtor_tbl[i].dtor)(val);
+    }
+  }
+}
+
+/*--------------- 7.25.2 Initialization functions ---------------*/
+// 7.25.2.1
+void call_once(once_flag *flag, void (*func)(void)) {
+  assert(flag && func);
+#    ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE
+  {
+    struct impl_call_once_param param;
+    param.func = func;
+    InitOnceExecuteOnce((PINIT_ONCE) flag, impl_call_once_callback,
+                        (PVOID) &param, NULL);
+  }
+#    else
+  if (InterlockedCompareExchangePointer(
+          (PVOID volatile *) &flag->status, (PVOID) 1, (PVOID) 0) ==
+      0) {
+    (func)();
+    InterlockedExchangePointer((PVOID volatile *) &flag->status,
+                               (PVOID) 2);
+  } else {
+    while (flag->status == 1) {
+      // busy loop!
+      thrd_yield();
+    }
+  }
+#    endif
+}
+
+/*------------- 7.25.3 Condition variable functions -------------*/
+// 7.25.3.1
+int cnd_broadcast(cnd_t *cond) {
+  assert(cond != NULL);
+  WakeAllConditionVariable((PCONDITION_VARIABLE) cond);
+  return thrd_success;
+}
+
+// 7.25.3.2
+void cnd_destroy(cnd_t *cond) {
+  (void) cond;
+  assert(cond != NULL);
+  // do nothing
+}
+
+// 7.25.3.3
+int cnd_init(cnd_t *cond) {
+  assert(cond != NULL);
+  InitializeConditionVariable((PCONDITION_VARIABLE) cond);
+  return thrd_success;
+}
+
+// 7.25.3.4
+int cnd_signal(cnd_t *cond) {
+  assert(cond != NULL);
+  WakeConditionVariable((PCONDITION_VARIABLE) cond);
+  return thrd_success;
+}
+
+// 7.25.3.5
+int cnd_timedwait(cnd_t *cond, mtx_t *mtx,
+                  const struct timespec *abs_time) {
+  assert(cond != NULL);
+  assert(mtx != NULL);
+  assert(abs_time != NULL);
+  const DWORD timeout = impl_abs2relmsec(abs_time);
+  if (SleepConditionVariableCS((PCONDITION_VARIABLE) cond,
+                               (PCRITICAL_SECTION) mtx, timeout))
+    return thrd_success;
+  return (GetLastError() == ERROR_TIMEOUT) ? thrd_timedout
+                                           : thrd_error;
+}
+
+// 7.25.3.6
+int cnd_wait(cnd_t *cond, mtx_t *mtx) {
+  assert(cond != NULL);
+  assert(mtx != NULL);
+  SleepConditionVariableCS((PCONDITION_VARIABLE) cond,
+                           (PCRITICAL_SECTION) mtx, INFINITE);
+  return thrd_success;
+}
+
+/*-------------------- 7.25.4 Mutex functions --------------------*/
+// 7.25.4.1
+void mtx_destroy(mtx_t *mtx) {
+  assert(mtx);
+  DeleteCriticalSection((PCRITICAL_SECTION) mtx);
+}
+
+// 7.25.4.2
+int mtx_init(mtx_t *mtx, int type) {
+  assert(mtx != NULL);
+  if (type != mtx_plain && type != mtx_timed &&
+      type != (mtx_plain | mtx_recursive) &&
+      type != (mtx_timed | mtx_recursive))
+    return thrd_error;
+  InitializeCriticalSection((PCRITICAL_SECTION) mtx);
+  return thrd_success;
+}
+
+// 7.25.4.3
+int mtx_lock(mtx_t *mtx) {
+  assert(mtx != NULL);
+  EnterCriticalSection((PCRITICAL_SECTION) mtx);
+  return thrd_success;
+}
+
+// 7.25.4.4
+int mtx_timedlock(mtx_t *mtx, const struct timespec *ts) {
+  assert(mtx != NULL);
+  assert(ts != NULL);
+  while (mtx_trylock(mtx) != thrd_success) {
+    if (impl_abs2relmsec(ts) == 0)
+      return thrd_timedout;
+    // busy loop!
+    thrd_yield();
+  }
+  return thrd_success;
+}
+
+// 7.25.4.5
+int mtx_trylock(mtx_t *mtx) {
+  assert(mtx != NULL);
+  return TryEnterCriticalSection((PCRITICAL_SECTION) mtx)
+             ? thrd_success
+             : thrd_busy;
+}
+
+// 7.25.4.6
+int mtx_unlock(mtx_t *mtx) {
+  assert(mtx != NULL);
+  LeaveCriticalSection((PCRITICAL_SECTION) mtx);
+  return thrd_success;
+}
+
+void __threads_win32_tls_callback(void) {
+  struct thrd_state *state = &impl_current_thread;
+  impl_tss_dtor_invoke();
+  if (state->handle_need_close) {
+    state->handle_need_close = false;
+    CloseHandle(state->thrd.handle);
+  }
+}
+
+/*------------------- 7.25.5 Thread functions -------------------*/
+// 7.25.5.1
+int thrd_create(thrd_t *thr, thrd_start_t func, void *arg) {
+  struct impl_thrd_param *pack;
+  uintptr_t               handle;
+  assert(thr != NULL);
+  pack = (struct impl_thrd_param *) malloc(
+      sizeof(struct impl_thrd_param));
+  if (!pack)
+    return thrd_nomem;
+  pack->func = func;
+  pack->arg  = arg;
+  handle     = _beginthreadex(NULL, 0, impl_thrd_routine, pack,
+                              CREATE_SUSPENDED, NULL);
+  if (handle == 0) {
+    free(pack);
+    if (errno == EAGAIN || errno == EACCES)
+      return thrd_nomem;
+    return thrd_error;
+  }
+  thr->handle = (void *) handle;
+  pack->thrd  = *thr;
+  ResumeThread((HANDLE) handle);
+  return thrd_success;
+}
+
+// 7.25.5.2
+thrd_t thrd_current(void) {
+  /* GetCurrentThread() returns a pseudo-handle, which we need
+   * to pass to DuplicateHandle(). Only the resulting handle can be
+   * used from other threads.
+   *
+   * Note that neither handle can be compared to the one by
+   * thread_create. Only the thread IDs - as returned by GetThreadId()
+   * and GetCurrentThreadId() can be compared directly.
+   *
+   * Other potential solutions would be:
+   * - define thrd_t as a thread Ids, but this would mean we'd need to
+   * OpenThread for many operations
+   * - use malloc'ed memory for thrd_t. This would imply using TLS for
+   * current thread.
+   *
+   * Neither is particularly nice.
+   *
+   * Life would be much easier if C11 threads had different
+   * abstractions for threads and thread IDs, just like C++11 threads
+   * does...
+   */
+  struct thrd_state *state = &impl_current_thread;
+  if (state->thrd.handle == NULL) {
+    if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(),
+                         GetCurrentProcess(), &(state->thrd.handle),
+                         0, FALSE, DUPLICATE_SAME_ACCESS)) {
+      abort();
+    }
+    state->handle_need_close = true;
+  }
+  return state->thrd;
+}
+
+// 7.25.5.3
+int thrd_detach(thrd_t thr) {
+  CloseHandle(thr.handle);
+  return thrd_success;
+}
+
+// 7.25.5.4
+int thrd_equal(thrd_t thr0, thrd_t thr1) {
+  return GetThreadId(thr0.handle) == GetThreadId(thr1.handle);
+}
+
+// 7.25.5.5
+_Noreturn void thrd_exit(int res) {
+  _endthreadex((unsigned) res);
+}
+
+// 7.25.5.6
+int thrd_join(thrd_t thr, int *res) {
+  DWORD w, code;
+  if (thr.handle == NULL) {
+    return thrd_error;
+  }
+  w = WaitForSingleObject(thr.handle, INFINITE);
+  if (w != WAIT_OBJECT_0)
+    return thrd_error;
+  if (res) {
+    if (!GetExitCodeThread(thr.handle, &code)) {
+      CloseHandle(thr.handle);
+      return thrd_error;
+    }
+    *res = (int) code;
+  }
+  CloseHandle(thr.handle);
+  return thrd_success;
+}
+
+// 7.25.5.7
+int thrd_sleep(const struct timespec *time_point,
+               struct timespec       *remaining) {
+  (void) remaining;
+  assert(time_point);
+  assert(!remaining); /* not implemented */
+  Sleep((DWORD) impl_timespec2msec(time_point));
+  return 0;
+}
+
+// 7.25.5.8
+void thrd_yield(void) {
+  SwitchToThread();
+}
+
+/*----------- 7.25.6 Thread-specific storage functions -----------*/
+// 7.25.6.1
+int tss_create(tss_t *key, tss_dtor_t dtor) {
+  assert(key != NULL);
+  *key = TlsAlloc();
+  if (dtor) {
+    if (impl_tss_dtor_register(*key, dtor)) {
+      TlsFree(*key);
+      return thrd_error;
+    }
+  }
+  return (*key != 0xFFFFFFFF) ? thrd_success : thrd_error;
+}
+
+// 7.25.6.2
+void tss_delete(tss_t key) {
+  TlsFree(key);
+}
+
+// 7.25.6.3
+void *tss_get(tss_t key) {
+  return TlsGetValue(key);
+}
+
+// 7.25.6.4
+int tss_set(tss_t key, void *val) {
+  return TlsSetValue(key, val) ? thrd_success : thrd_error;
+}
+
+#  endif
+#endif /* KIT_DISABLE_SYSTEM_THREADS */
diff --git a/source/kit/threads.win32.h b/source/kit/threads.win32.h
new file mode 100644
index 0000000..e8e9abf
--- /dev/null
+++ b/source/kit/threads.win32.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2022 Yonggang Luo
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef KIT_THREADS_WIN32_H
+#define KIT_THREADS_WIN32_H
+
+#ifndef KIT_DISABLE_SYSTEM_THREADS
+
+#  ifdef __cplusplus
+extern "C" {
+#  endif
+
+#  if defined(_WIN32) && !defined(__CYGWIN__)
+void __threads_win32_tls_callback(void);
+#  endif
+
+#  ifdef __cplusplus
+}
+#  endif
+
+#endif
+
+#endif
diff --git a/source/kit/threads_tls_callback.win32.cpp b/source/kit/threads_tls_callback.win32.cpp
new file mode 100644
index 0000000..3cc3cc6
--- /dev/null
+++ b/source/kit/threads_tls_callback.win32.cpp
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2022 Yonggang Luo
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef KIT_DISABLE_SYSTEM_THREADS
+#  if defined(_WIN32) && !defined(__CYGWIN__)
+
+#    include "threads.win32.h"
+
+struct tls_callback {
+  tls_callback() { }
+  ~tls_callback() {
+    __threads_win32_tls_callback();
+  }
+};
+
+static thread_local tls_callback tls_callback_instance;
+
+#  endif
+#endif
diff --git a/source/kit/time.c b/source/kit/time.c
new file mode 100644
index 0000000..bcb3819
--- /dev/null
+++ b/source/kit/time.c
@@ -0,0 +1,84 @@
+/*
+ * C11 <time.h> implementation
+ *
+ * (C) Copyright yohhoy 2012.
+ * Copyright 2022 Yonggang Luo
+ * Distributed under the Boost Software License, Version 1.0.
+ *
+ * Permission is hereby granted, free of charge, to any person or
+ * organization obtaining a copy of the software and accompanying
+ * documentation covered by this license (the "Software") to use,
+ * reproduce, display, distribute, execute, and transmit the Software,
+ * and to prepare [[derivative work]]s of the Software, and to permit
+ * third-parties to whom the Software is furnished to do so, all
+ * subject to the following:
+ *
+ * The copyright notices in the Software and this entire statement,
+ * including the above license grant, this restriction and the
+ * following disclaimer, must be included in all copies of the
+ * Software, in whole or in part, and all derivative works of the
+ * Software, unless such copies or derivative works are solely in the
+ * form of machine-executable object code generated by a source
+ * language processor.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
+ * NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE
+ * DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "time.h"
+
+#ifndef KIT_HAVE_TIMESPEC_GET
+
+#  if defined(_WIN32) && !defined(__CYGWIN__)
+
+#    ifndef WIN32_LEAN_AND_MEAN
+#      define WIN32_LEAN_AND_MEAN 1
+#    endif
+#    include <windows.h>
+
+int timespec_get(struct timespec *ts, int base) {
+/* difference between 1970 and 1601 */
+#    define _TIMESPEC_IMPL_UNIX_EPOCH_IN_TICKS 116444736000000000ull
+/* 1 tick is 100 nanoseconds */
+#    define _TIMESPEC_IMPL_TICKS_PER_SECONDS 10000000ull
+  if (!ts)
+    return 0;
+  if (base == TIME_UTC) {
+    FILETIME       ft;
+    ULARGE_INTEGER date;
+    LONGLONG       ticks;
+
+    GetSystemTimeAsFileTime(&ft);
+    date.HighPart = ft.dwHighDateTime;
+    date.LowPart  = ft.dwLowDateTime;
+    ticks         = (LONGLONG) (date.QuadPart -
+                        _TIMESPEC_IMPL_UNIX_EPOCH_IN_TICKS);
+    ts->tv_sec    = ticks / _TIMESPEC_IMPL_TICKS_PER_SECONDS;
+    ts->tv_nsec   = (ticks % _TIMESPEC_IMPL_TICKS_PER_SECONDS) * 100;
+    return base;
+  }
+  return 0;
+#    undef _TIMESPEC_IMPL_UNIX_EPOCH_IN_TICKS
+#    undef _TIMESPEC_IMPL_TICKS_PER_SECONDS
+}
+
+#  else
+
+int timespec_get(struct timespec *ts, int base) {
+  if (!ts)
+    return 0;
+  if (base == TIME_UTC) {
+    clock_gettime(CLOCK_REALTIME, ts);
+    return base;
+  }
+  return 0;
+}
+#  endif
+
+#endif
diff --git a/source/kit/time.h b/source/kit/time.h
new file mode 100644
index 0000000..dbf8883
--- /dev/null
+++ b/source/kit/time.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2022 Yonggang Luo
+ * SPDX-License-Identifier: MIT
+ *
+ * C11 <time.h> emulation library
+ */
+
+#ifndef KIT_TIME_H
+#define KIT_TIME_H
+
+#include <time.h>
+
+/*---------------------------- macros ---------------------------*/
+
+#ifndef TIME_UTC
+#  define TIME_UTC 1
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*---------------------------- types ----------------------------*/
+
+/*
+ * On MINGW `struct timespec` present but `timespec_get` may not
+ * present; On MSVC `struct timespec` and `timespec_get` present at
+ * the same time; So detecting `HAVE_STRUCT_TIMESPEC` in meson script
+ * dynamically.
+ */
+#ifndef KIT_HAVE_STRUCT_TIMESPEC
+struct timespec {
+  time_t tv_sec;  // Seconds - >= 0
+  long   tv_nsec; // Nanoseconds - [0, 999999999]
+};
+#endif
+
+/*-------------------------- functions --------------------------*/
+
+#if !defined(KIT_HAVE_TIMESPEC_GET)
+#  define KIT_HAVE_TIMESPEC_GET_NEED_DECL_
+#elif defined(__APPLE__) && defined(__cplusplus) && \
+    (__cplusplus < 201703L)
+/* On macOS, the guard for declaration of timespec_get is by
+ * (defined(__cplusplus) && __cplusplus >= 201703L),
+ * fix the declaration for C++14 and lower here
+ */
+#  define KIT_HAVE_TIMESPEC_GET_NEED_DECL_
+#endif
+
+#ifdef KIT_HAVE_TIMESPEC_GET_NEED_DECL_
+/*-------------------- 7.25.7 Time functions --------------------*/
+// 7.25.6.1
+int timespec_get(struct timespec *ts, int base);
+#  undef KIT_HAVE_TIMESPEC_GET_NEED_DECL_
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* KIT_TIME_H */
diff --git a/source/test/unittests/CMakeLists.txt b/source/test/unittests/CMakeLists.txt
index 5ec950d..e64628d 100644
--- a/source/test/unittests/CMakeLists.txt
+++ b/source/test/unittests/CMakeLists.txt
@@ -1,7 +1,8 @@
 target_sources(
   ${KIT_TEST_SUITE}
     PRIVATE
-      async_function.test.c test_duration.test.c main.test.c
-      string_ref.test.c atomic.test.c array_ref.test.c input_stream.test.c
-      lower_bound.test.c mersenne_twister_64.test.c input_buffer.test.c
+      async_function.test.c mutex.test.c test_duration.test.c
+      main.test.c string_ref.test.c atomic.test.c thread.test.c
+      array_ref.test.c input_stream.test.c lower_bound.test.c
+      condition_variable.test.c mersenne_twister_64.test.c input_buffer.test.c
       dynamic_array.test.c)
diff --git a/source/test/unittests/c11_threads.c b/source/test/unittests/c11_threads.c
deleted file mode 100644
index cd50846..0000000
--- a/source/test/unittests/c11_threads.c
+++ /dev/null
@@ -1,6 +0,0 @@
-#include "../../kit/c11/threads.h"
-
-#define KIT_TEST_FILE atomic
-#include "../../kit_test/test.h"
-
-TEST("c11 threads") { }
diff --git a/source/test/unittests/condition_variable.test.c b/source/test/unittests/condition_variable.test.c
new file mode 100644
index 0000000..c9d818a
--- /dev/null
+++ b/source/test/unittests/condition_variable.test.c
@@ -0,0 +1,60 @@
+#include "../../kit/threads.h"
+
+#define KIT_TEST_FILE condition_variable
+#include "../../kit_test/test.h"
+
+typedef struct {
+  mtx_t m;
+  cnd_t send;
+  cnd_t receive;
+  int   value;
+} test_data_t;
+
+static int test_run(void *p) {
+  test_data_t *data = (test_data_t *) p;
+
+  mtx_lock(&data->m);
+
+  data->value = 20;
+  mtx_unlock(&data->m);
+  cnd_broadcast(&data->send);
+
+  cnd_wait(&data->receive, &data->m);
+
+  data->value = 22;
+  cnd_broadcast(&data->send);
+
+  mtx_unlock(&data->m);
+
+  return 0;
+}
+
+TEST("condition variable") {
+  test_data_t data;
+  REQUIRE(mtx_init(&data.m, mtx_plain) == thrd_success);
+  REQUIRE(cnd_init(&data.send) == thrd_success);
+  REQUIRE(cnd_init(&data.receive) == thrd_success);
+  data.value = 0;
+
+  thrd_t t;
+  REQUIRE(thrd_create(&t, test_run, &data) == thrd_success);
+
+  REQUIRE(mtx_lock(&data.m) == thrd_success);
+
+  REQUIRE(cnd_wait(&data.send, &data.m) == thrd_success);
+  int x = data.value;
+
+  REQUIRE(cnd_broadcast(&data.receive) == thrd_success);
+
+  REQUIRE(cnd_wait(&data.send, &data.m) == thrd_success);
+  x += data.value;
+
+  REQUIRE(mtx_unlock(&data.m) == thrd_success);
+  REQUIRE(thrd_join(t, NULL) == thrd_success);
+
+  mtx_destroy(&data.m);
+  cnd_destroy(&data.send);
+  cnd_destroy(&data.receive);
+
+  REQUIRE(x == 42);
+}
diff --git a/source/test/unittests/mutex.test.c b/source/test/unittests/mutex.test.c
new file mode 100644
index 0000000..c0153e7
--- /dev/null
+++ b/source/test/unittests/mutex.test.c
@@ -0,0 +1,74 @@
+#include "../../kit/threads.h"
+
+#define KIT_TEST_FILE mutex
+#include "../../kit_test/test.h"
+
+enum { THREAD_COUNT = 200 };
+
+typedef struct {
+  mtx_t lock;
+  int   value;
+} test_data_t;
+
+static int test_run(void *data) {
+  test_data_t *x = (test_data_t *) data;
+  for (int i = 0; i < 1000; i++) {
+    mtx_lock(&x->lock);
+
+    x->value += i;
+    thrd_yield();
+    x->value -= i + 42;
+    thrd_yield();
+    x->value += i + 20;
+    thrd_yield();
+    x->value += 22 - i;
+
+    mtx_unlock(&x->lock);
+  }
+  return 0;
+}
+
+int test_lock_for_2_sec(void *data) {
+  mtx_t *m = (mtx_t *) data;
+  mtx_lock(m);
+
+  struct timespec sec = { .tv_sec = 2, .tv_nsec = 0 };
+  thrd_sleep(&sec, NULL);
+
+  mtx_unlock(m);
+}
+
+TEST("mutex lock") {
+  test_data_t data;
+  thrd_t      pool[THREAD_COUNT];
+  data.value = 42;
+  REQUIRE(mtx_init(&data.lock, mtx_plain) == thrd_success);
+
+  for (ptrdiff_t i = 0; i < THREAD_COUNT; i++)
+    thrd_create(pool + i, test_run, &data);
+  for (ptrdiff_t i = 0; i < THREAD_COUNT; i++)
+    thrd_join(pool[i], NULL);
+
+  mtx_destroy(&data.lock);
+  REQUIRE(data.value == 42);
+}
+
+TEST("mutex try lock") {
+  mtx_t m;
+  REQUIRE(mtx_init(&m, mtx_plain) == thrd_success);
+
+  thrd_t t;
+  REQUIRE(thrd_create(&t, test_lock_for_2_sec, &m) == thrd_success);
+
+  struct timespec sec = { .tv_sec = 1, .tv_nsec = 0 };
+  REQUIRE(thrd_sleep(&sec, NULL) == thrd_success);
+
+  REQUIRE(mtx_trylock(&m) == thrd_busy);
+
+  REQUIRE(thrd_join(t, NULL) == thrd_success);
+
+  REQUIRE(mtx_trylock(&m) == thrd_success);
+  REQUIRE(mtx_unlock(&m) == thrd_success);
+
+  mtx_destroy(&m);
+}
diff --git a/source/test/unittests/thread.test.c b/source/test/unittests/thread.test.c
new file mode 100644
index 0000000..44e51b5
--- /dev/null
+++ b/source/test/unittests/thread.test.c
@@ -0,0 +1,78 @@
+#include "../../kit/threads.h"
+
+#define KIT_TEST_FILE thread
+#include "../../kit_test/test.h"
+
+static int test_nothing(void *) { }
+
+static int test_run(void *data) {
+  int *n = (int *) data;
+  return *n + 20;
+}
+
+static int test_exit(void *data) {
+  int *n = (int *) data;
+
+  *n = 1;
+  thrd_exit(3);
+  *n = 2;
+  return 4;
+}
+
+static int test_yield(void *data) {
+  thrd_yield();
+}
+
+static int test_sleep(void *data) {
+  struct timespec t = { .tv_sec = 0, .tv_nsec = 10000000 };
+  thrd_sleep(&t, NULL);
+}
+
+TEST("thread run") {
+  thrd_t t;
+  int    data = 22;
+  int    result;
+  REQUIRE(thrd_create(&t, test_run, &data) == thrd_success);
+  REQUIRE(thrd_join(t, &result) == thrd_success);
+  REQUIRE(result == 42);
+}
+
+TEST("thread equal") {
+  thrd_t foo, bar;
+  REQUIRE(thrd_create(&foo, test_nothing, NULL) == thrd_success);
+  REQUIRE(thrd_create(&bar, test_nothing, NULL) == thrd_success);
+  REQUIRE(thrd_equal(foo, foo));
+  REQUIRE(!thrd_equal(foo, bar));
+  REQUIRE(!thrd_equal(foo, thrd_current()));
+}
+
+TEST("thread exit") {
+  thrd_t foo;
+  int    data;
+  int    result;
+  REQUIRE(thrd_create(&foo, test_exit, &data) == thrd_success);
+  REQUIRE(thrd_join(foo, &result) == thrd_success);
+  REQUIRE(data == 1);
+  REQUIRE(result == 3);
+}
+
+TEST("thread yield") {
+  thrd_t foo;
+  REQUIRE(thrd_create(&foo, test_yield, NULL) == thrd_success);
+  REQUIRE(thrd_join(foo, NULL) == thrd_success);
+}
+
+TEST("thread sleep") {
+  thrd_t foo;
+  REQUIRE(thrd_create(&foo, test_sleep, NULL) == thrd_success);
+  REQUIRE(thrd_join(foo, NULL) == thrd_success);
+}
+
+TEST("thread detach") {
+  thrd_t foo;
+  REQUIRE(thrd_create(&foo, test_nothing, NULL) == thrd_success);
+  REQUIRE(thrd_detach(foo) == thrd_success);
+
+  struct timespec t = { .tv_sec = 0, .tv_nsec = 10000000 };
+  thrd_sleep(&t, NULL);
+}
-- 
cgit v1.2.3