From 251f92ede2255d7f52667a0d5794136f5da2b8c6 Mon Sep 17 00:00:00 2001
From: Mitya Selivanov <automainint@guattari.tech>
Date: Sun, 18 Dec 2022 00:12:32 +0100
Subject: Threads cleanup

---
 source/kit/CMakeLists.txt                        |   6 +-
 source/kit/condition_variable.h                  |  35 +-
 source/kit/mersenne_twister_64.c                 |   8 -
 source/kit/mersenne_twister_64.h                 |   2 -
 source/kit/mutex.h                               |  44 ++-
 source/kit/secure_random.c                       |  23 +-
 source/kit/secure_random.h                       |   5 -
 source/kit/thread.h                              |  92 +++++
 source/kit/thread.posix.c                        | 266 ++++++++++++++
 source/kit/thread.win32.c                        | 380 ++++++++++++++++++++
 source/kit/threads.h                             | 197 -----------
 source/kit/threads.posix.c                       | 322 -----------------
 source/kit/threads.win32.c                       | 433 -----------------------
 source/kit/time.c                                |  43 +--
 source/kit/time.h                                |  43 +--
 source/test/unittests/atomic.test.c              |   2 +-
 source/test/unittests/condition_variable.test.c  |   1 +
 source/test/unittests/mersenne_twister_64.test.c |   4 +-
 source/test/unittests/mutex.test.c               |   1 +
 source/test/unittests/secure_random.test.c       |   4 -
 source/test/unittests/thread.test.c              |   2 +-
 21 files changed, 845 insertions(+), 1068 deletions(-)
 create mode 100644 source/kit/thread.h
 create mode 100644 source/kit/thread.posix.c
 create mode 100644 source/kit/thread.win32.c
 delete mode 100644 source/kit/threads.h
 delete mode 100644 source/kit/threads.posix.c
 delete mode 100644 source/kit/threads.win32.c

(limited to 'source')

diff --git a/source/kit/CMakeLists.txt b/source/kit/CMakeLists.txt
index f9a30d2..e12077a 100644
--- a/source/kit/CMakeLists.txt
+++ b/source/kit/CMakeLists.txt
@@ -1,8 +1,8 @@
 target_sources(
   ${KIT_LIBRARY}
     PRIVATE
-      input_buffer.c bigint.c status.c threads.win32.c time.c
-      secure_random.c atomic.win32.c threads.posix.c condition_variable.c
+      input_buffer.c bigint.c status.c time.c secure_random.c
+      thread.posix.c atomic.win32.c condition_variable.c thread.win32.c
       move_back.c input_stream.c lower_bound.c file.c string_ref.c
       async_function.c allocator.c array_ref.c dynamic_array.c mutex.c
       mersenne_twister_64.c
@@ -13,7 +13,7 @@ target_sources(
       $<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}/thread.h>
       $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/bigint.h>
       $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/dynamic_array.h>
       $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/condition_variable.h>
diff --git a/source/kit/condition_variable.h b/source/kit/condition_variable.h
index dd3b891..0e4630d 100644
--- a/source/kit/condition_variable.h
+++ b/source/kit/condition_variable.h
@@ -1,6 +1,39 @@
 #ifndef KIT_CONDITION_VARIABLE_H
 #define KIT_CONDITION_VARIABLE_H
 
-#include "threads.h"
+#ifndef KIT_DISABLE_SYSTEM_THREADS
+#  include "mutex.h"
+
+#  ifdef __cplusplus
+extern "C" {
+#  endif
+
+#  if defined(_WIN32) && !defined(__CYGWIN__)
+typedef struct {
+  void *Ptr;
+} cnd_t;
+
+typedef struct {
+  volatile uintptr_t status;
+} once_flag;
+#  else
+typedef pthread_cond_t cnd_t;
+typedef pthread_once_t once_flag;
+#  endif
+
+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_);
+
+#  ifdef __cplusplus
+}
+#  endif
+
+#endif
 
 #endif
diff --git a/source/kit/mersenne_twister_64.c b/source/kit/mersenne_twister_64.c
index 28f8b3b..0570a1f 100644
--- a/source/kit/mersenne_twister_64.c
+++ b/source/kit/mersenne_twister_64.c
@@ -1,7 +1,5 @@
 #include "mersenne_twister_64.h"
 
-#include "secure_random.h"
-
 #define MM 156
 #define MATRIX_A 0xb5026f5aa96619e9ull
 #define UM 0xffffffff80000000ull
@@ -60,9 +58,3 @@ uint64_t kit_mt64_generate(kit_mt64_state_t *const state) {
 
   return x;
 }
-
-uint64_t kit_mt64_seed() {
-  uint64_t seed;
-  kit_secure_random(sizeof seed, &seed);
-  return seed;
-}
diff --git a/source/kit/mersenne_twister_64.h b/source/kit/mersenne_twister_64.h
index fb509c2..67a4d22 100644
--- a/source/kit/mersenne_twister_64.h
+++ b/source/kit/mersenne_twister_64.h
@@ -24,8 +24,6 @@ void kit_mt64_init(kit_mt64_state_t *state, uint64_t seed);
 
 uint64_t kit_mt64_generate(kit_mt64_state_t *state);
 
-uint64_t kit_mt64_seed();
-
 #ifndef KIT_DISABLE_SHORT_NAMES
 #  define mt64_state_t kit_mt64_state_t
 #  define mt64_init_array kit_mt64_init_array
diff --git a/source/kit/mutex.h b/source/kit/mutex.h
index 0b7c548..e5d2e40 100644
--- a/source/kit/mutex.h
+++ b/source/kit/mutex.h
@@ -1,6 +1,48 @@
 #ifndef KIT_MUTEX_H
 #define KIT_MUTEX_H
 
-#include "threads.h"
+#ifndef KIT_DISABLE_SYSTEM_THREADS
+#  include "time.h"
+
+#  if !defined(_WIN32) || defined(__CYGWIN__)
+#    include <pthread.h>
+#  endif
+
+#  ifdef __cplusplus
+extern "C" {
+#  endif
+
+#  if defined(_WIN32) && !defined(__CYGWIN__)
+typedef struct {
+  void     *DebugInfo;
+  long      LockCount;
+  long      RecursionCount;
+  void     *OwningThread;
+  void     *LockSemaphore;
+  uintptr_t SpinCount;
+} mtx_t;
+#  else
+typedef pthread_mutex_t mtx_t;
+#  endif
+
+enum {
+  mtx_plain     = 0,
+  mtx_recursive = 1,
+  mtx_timed     = 2,
+};
+
+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_);
+
+#  ifdef __cplusplus
+}
+#  endif
+
+#endif
 
 #endif
diff --git a/source/kit/secure_random.c b/source/kit/secure_random.c
index 8f48c9d..013fd52 100644
--- a/source/kit/secure_random.c
+++ b/source/kit/secure_random.c
@@ -1,8 +1,10 @@
 #include "secure_random.h"
 
+#include "condition_variable.h"
 #include "mersenne_twister_64.h"
 #include "mutex.h"
 #include "time.h"
+#include <assert.h>
 #include <stdio.h>
 
 #if defined(_WIN32) && !defined(__CYGWIN__)
@@ -28,12 +30,19 @@ static uint64_t get_available_memory() {
 }
 
 #ifndef KIT_DISABLE_SYSTEM_THREADS
-static mtx_t kit_secure_random_fallback_mutex;
+static once_flag kit_secure_random_fallback_flag;
+static mtx_t     kit_secure_random_fallback_mutex;
+
+static void secure_random_fallback_init() {
+  mtx_init(&kit_secure_random_fallback_mutex, mtx_plain);
+}
 #endif
 
 static void secure_random_fallback(ptrdiff_t const size,
                                    void *const     data) {
 #ifndef KIT_DISABLE_SYSTEM_THREADS
+  call_once(&kit_secure_random_fallback_flag,
+            secure_random_fallback_init);
   mtx_lock(&kit_secure_random_fallback_mutex);
 #endif
 
@@ -82,18 +91,6 @@ static void secure_random_fallback(ptrdiff_t const size,
 #endif
 }
 
-void kit_secure_random_init() {
-#ifndef KIT_DISABLE_SYSTEM_THREADS
-  mtx_init(&kit_secure_random_fallback_mutex, mtx_plain);
-#endif
-}
-
-void kit_secure_random_cleanup() {
-#ifndef KIT_DISABLE_SYSTEM_THREADS
-  mtx_destroy(&kit_secure_random_fallback_mutex);
-#endif
-}
-
 void kit_secure_random(ptrdiff_t const size, void *const data) {
   if (size <= 0)
     return;
diff --git a/source/kit/secure_random.h b/source/kit/secure_random.h
index 5b461f9..758478d 100644
--- a/source/kit/secure_random.h
+++ b/source/kit/secure_random.h
@@ -9,14 +9,9 @@
 extern "C" {
 #endif
 
-void kit_secure_random_init();
-void kit_secure_random_cleanup();
-
 void kit_secure_random(ptrdiff_t size, void *data);
 
 #ifndef KIT_DISABLE_SHORT_NAMES
-#  define secure_random_init kit_secure_random_init
-#  define secure_random_cleanup kit_secure_random_cleanup
 #  define secure_random kit_secure_random
 #endif
 
diff --git a/source/kit/thread.h b/source/kit/thread.h
new file mode 100644
index 0000000..f64264e
--- /dev/null
+++ b/source/kit/thread.h
@@ -0,0 +1,92 @@
+#ifndef KIT_THREAD_H
+#define KIT_THREAD_H
+
+#ifndef KIT_DISABLE_SYSTEM_THREADS
+#  include "time.h"
+#  include <stddef.h>
+
+#  if defined(__cplusplus)
+#    define _Noreturn [[noreturn]]
+#  elif defined(_MSC_VER)
+#    define _Noreturn __declspec(noreturn)
+#  endif
+
+#  if !defined(_WIN32) || defined(__CYGWIN__)
+#    include <pthread.h>
+#  endif
+
+#  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
+
+typedef void (*tss_dtor_t)(void *);
+typedef int (*thrd_start_t)(void *);
+
+#  if defined(_WIN32) && !defined(__CYGWIN__)
+typedef struct {
+  void *handle;
+} thrd_t;
+typedef unsigned long tss_t;
+#  else
+typedef pthread_t     thrd_t;
+typedef pthread_key_t tss_t;
+#  endif
+
+enum {
+  thrd_success = 0,
+  thrd_timedout,
+  thrd_error,
+  thrd_busy,
+  thrd_nomem,
+  thrd_wrong_stack_size
+};
+
+int            thrd_create(thrd_t *, thrd_start_t, void *);
+int            thrd_create_with_stack(thrd_t *, thrd_start_t, void *,
+                                      ptrdiff_t stack_size);
+thrd_t         thrd_current(void);
+int            thrd_detach(thrd_t);
+int            thrd_equal(thrd_t, thrd_t);
+_Noreturn void thrd_exit(int);
+int            thrd_join(thrd_t, int *);
+int            thrd_sleep(const struct timespec *, struct timespec *);
+void           thrd_yield(void);
+
+#  ifdef __cplusplus
+}
+#  endif
+
+#endif
+
+#endif
diff --git a/source/kit/thread.posix.c b/source/kit/thread.posix.c
new file mode 100644
index 0000000..442d7f8
--- /dev/null
+++ b/source/kit/thread.posix.c
@@ -0,0 +1,266 @@
+#ifndef KIT_DISABLE_SYSTEM_THREADS
+#  if !defined(_WIN32) || defined(__CYGWIN__)
+
+#    include <assert.h>
+#    include <errno.h>
+#    include <limits.h>
+#    include <sched.h>
+#    include <stdint.h> /* intptr_t */
+#    include <stdlib.h>
+#    include <unistd.h>
+
+#    include "allocator.h"
+#    include "condition_variable.h"
+#    include "mutex.h"
+#    include "thread.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
+
+/*
+Implementation limits:
+  - Conditionally emulation for "mutex with timeout"
+    (see EMULATED_THREADS_USE_NATIVE_TIMEDLOCK macro)
+*/
+typedef struct {
+  thrd_start_t    func;
+  void           *arg;
+  kit_allocator_t alloc;
+} impl_thrd_param_t;
+
+static void *impl_thrd_routine(void *p) {
+  impl_thrd_param_t pack = *((impl_thrd_param_t *) p);
+  pack.alloc.deallocate(pack.alloc.state, p);
+  return (void *) (intptr_t) pack.func(pack.arg);
+}
+
+void call_once(once_flag *flag, void (*func)(void)) {
+  pthread_once(flag, func);
+}
+
+int cnd_broadcast(cnd_t *cond) {
+  assert(cond != NULL);
+  return (pthread_cond_broadcast(cond) == 0) ? thrd_success
+                                             : thrd_error;
+}
+
+void cnd_destroy(cnd_t *cond) {
+  assert(cond);
+  pthread_cond_destroy(cond);
+}
+
+int cnd_init(cnd_t *cond) {
+  assert(cond != NULL);
+  return (pthread_cond_init(cond, NULL) == 0) ? thrd_success
+                                              : thrd_error;
+}
+
+int cnd_signal(cnd_t *cond) {
+  assert(cond != NULL);
+  return (pthread_cond_signal(cond) == 0) ? thrd_success : thrd_error;
+}
+
+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;
+}
+
+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;
+}
+
+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
+
+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;
+}
+
+int mtx_lock(mtx_t *mtx) {
+  assert(mtx != NULL);
+  return (pthread_mutex_lock(mtx) == 0) ? thrd_success : thrd_error;
+}
+
+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
+  }
+}
+
+int mtx_trylock(mtx_t *mtx) {
+  assert(mtx != NULL);
+  return (pthread_mutex_trylock(mtx) == 0) ? thrd_success : thrd_busy;
+}
+
+int mtx_unlock(mtx_t *mtx) {
+  assert(mtx != NULL);
+  return (pthread_mutex_unlock(mtx) == 0) ? thrd_success : thrd_error;
+}
+
+int thrd_create_with_stack(thrd_t *thr, thrd_start_t func, void *arg,
+                           ptrdiff_t const require_stack_size) {
+  impl_thrd_param_t *pack;
+  assert(thr != NULL);
+  assert(require_stack_size == 0 ||
+         require_stack_size >= PTHREAD_STACK_MIN);
+  pthread_attr_t  attr;
+  pthread_attr_t *attr_p = NULL;
+  if (require_stack_size > 0) {
+    ptrdiff_t const page_size  = (ptrdiff_t) sysconf(_SC_PAGESIZE);
+    ptrdiff_t const delta      = require_stack_size % page_size;
+    ptrdiff_t const stack_size = delta == 0 ? require_stack_size
+                                            : require_stack_size +
+                                                  page_size - delta;
+    if (pthread_attr_init(&attr) != 0)
+      return thrd_nomem;
+    if (pthread_attr_setstacksize(&attr, (size_t) stack_size) != 0)
+      return thrd_wrong_stack_size;
+    attr_p = &attr;
+  }
+  kit_allocator_t alloc = kit_alloc_default();
+  pack                  = (impl_thrd_param_t *) alloc.allocate(
+                       alloc.state, sizeof(impl_thrd_param_t));
+  if (!pack) {
+    if (attr_p)
+      pthread_attr_destroy(attr_p);
+    return thrd_nomem;
+  }
+  pack->func  = func;
+  pack->arg   = arg;
+  pack->alloc = alloc;
+  if (pthread_create(thr, attr_p, impl_thrd_routine, pack) != 0) {
+    alloc.deallocate(alloc.state, pack);
+    if (attr_p)
+      pthread_attr_destroy(attr_p);
+    return thrd_error;
+  }
+  if (attr_p)
+    pthread_attr_destroy(attr_p);
+  return thrd_success;
+}
+
+int thrd_create(thrd_t *thr, thrd_start_t func, void *arg) {
+  return thrd_create_with_stack(thr, func, arg, 0);
+}
+thrd_t thrd_current(void) {
+  return pthread_self();
+}
+
+int thrd_detach(thrd_t thr) {
+  return (pthread_detach(thr) == 0) ? thrd_success : thrd_error;
+}
+
+int thrd_equal(thrd_t thr0, thrd_t thr1) {
+  return pthread_equal(thr0, thr1);
+}
+
+_Noreturn void thrd_exit(int res) {
+  pthread_exit((void *) (intptr_t) res);
+}
+
+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;
+}
+
+int thrd_sleep(const struct timespec *time_point,
+               struct timespec       *remaining) {
+  assert(time_point != NULL);
+  return nanosleep(time_point, remaining);
+}
+
+void thrd_yield(void) {
+  sched_yield();
+}
+
+#  endif
+#endif
diff --git a/source/kit/thread.win32.c b/source/kit/thread.win32.c
new file mode 100644
index 0000000..7e1dda8
--- /dev/null
+++ b/source/kit/thread.win32.c
@@ -0,0 +1,380 @@
+#ifndef KIT_DISABLE_SYSTEM_THREADS
+#  if defined(_WIN32) && !defined(__CYGWIN__)
+
+#    include <assert.h>
+#    include <errno.h>
+#    include <limits.h>
+#    include <process.h>
+#    include <stdbool.h>
+#    include <stdlib.h>
+
+#    include "allocator.h"
+#    include "condition_variable.h"
+#    include "mutex.h"
+#    include "threads.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*
+*/
+
+typedef struct {
+  thrd_start_t    func;
+  void           *arg;
+  thrd_t          thrd;
+  kit_allocator_t alloc;
+} impl_thrd_param_t;
+
+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) {
+  impl_thrd_param_t *pack_p = (impl_thrd_param_t *) p;
+  impl_thrd_param_t  pack;
+  int                code;
+  impl_current_thread.thrd              = pack_p->thrd;
+  impl_current_thread.handle_need_close = false;
+  memcpy(&pack, pack_p, sizeof(impl_thrd_param_t));
+  pack.alloc.deallocate(pack.alloc.state, 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);
+    }
+  }
+}
+
+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
+}
+
+int cnd_broadcast(cnd_t *cond) {
+  assert(cond != NULL);
+  WakeAllConditionVariable((PCONDITION_VARIABLE) cond);
+  return thrd_success;
+}
+
+void cnd_destroy(cnd_t *cond) {
+  assert(cond != NULL);
+  /* do nothing */
+  (void) cond;
+}
+
+int cnd_init(cnd_t *cond) {
+  assert(cond != NULL);
+  InitializeConditionVariable((PCONDITION_VARIABLE) cond);
+  return thrd_success;
+}
+
+int cnd_signal(cnd_t *cond) {
+  assert(cond != NULL);
+  WakeConditionVariable((PCONDITION_VARIABLE) cond);
+  return thrd_success;
+}
+
+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;
+}
+
+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;
+}
+
+void mtx_destroy(mtx_t *mtx) {
+  assert(mtx);
+  DeleteCriticalSection((PCRITICAL_SECTION) mtx);
+}
+
+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;
+}
+
+int mtx_lock(mtx_t *mtx) {
+  assert(mtx != NULL);
+  EnterCriticalSection((PCRITICAL_SECTION) mtx);
+  return thrd_success;
+}
+
+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;
+}
+
+int mtx_trylock(mtx_t *mtx) {
+  assert(mtx != NULL);
+  return TryEnterCriticalSection((PCRITICAL_SECTION) mtx)
+             ? thrd_success
+             : thrd_busy;
+}
+
+int mtx_unlock(mtx_t *mtx) {
+  assert(mtx != NULL);
+  LeaveCriticalSection((PCRITICAL_SECTION) mtx);
+  return thrd_success;
+}
+
+int thrd_create_with_stack(thrd_t *thr, thrd_start_t func, void *arg,
+                           ptrdiff_t const stack_size) {
+  impl_thrd_param_t *pack;
+  uintptr_t          handle;
+  assert(thr != NULL);
+  assert(stack_size >= 0 && stack_size < 0x100000000);
+  kit_allocator_t alloc = kit_alloc_default();
+  pack                  = (impl_thrd_param_t *) alloc.allocate(
+                       alloc.state, (sizeof(impl_thrd_param_t)));
+  if (!pack)
+    return thrd_nomem;
+  pack->func  = func;
+  pack->arg   = arg;
+  pack->alloc = alloc;
+  handle      = _beginthreadex(NULL, (unsigned) stack_size,
+                               impl_thrd_routine, pack, CREATE_SUSPENDED,
+                               NULL);
+  if (handle == 0) {
+    alloc.deallocate(alloc.state, 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;
+}
+
+int thrd_create(thrd_t *thr, thrd_start_t func, void *arg) {
+  return thrd_create_with_stack(thr, func, arg, 0);
+}
+
+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;
+}
+
+int thrd_detach(thrd_t thr) {
+  CloseHandle(thr.handle);
+  return thrd_success;
+}
+
+int thrd_equal(thrd_t thr0, thrd_t thr1) {
+  return GetThreadId(thr0.handle) == GetThreadId(thr1.handle);
+}
+
+_Noreturn void thrd_exit(int res) {
+  _endthreadex((unsigned) res);
+}
+
+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;
+}
+
+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;
+}
+
+void thrd_yield(void) {
+  SwitchToThread();
+}
+
+#  endif
+#endif
diff --git a/source/kit/threads.h b/source/kit/threads.h
deleted file mode 100644
index 5cfb5d3..0000000
--- a/source/kit/threads.h
+++ /dev/null
@@ -1,197 +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_THREADS_H
-#define KIT_THREADS_H
-
-#ifndef KIT_DISABLE_SYSTEM_THREADS
-#  include "time.h"
-
-#  include <errno.h>
-#  include <limits.h>
-#  include <stddef.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 */
-#  else
-#    include <pthread.h>
-#    include <unistd.h> /* close, _exit */
-#  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
-#  else
-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
-#  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,
-  thrd_wrong_stack_size
-};
-
-/*-------------------------- 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 *);
-int    thrd_create_with_stack(thrd_t *, thrd_start_t, void *,
-                              ptrdiff_t stack_size);
-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);
-
-#  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
deleted file mode 100644
index efedbaa..0000000
--- a/source/kit/threads.posix.c
+++ /dev/null
@@ -1,322 +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_DISABLE_SYSTEM_THREADS
-#  if !defined(_WIN32) || defined(__CYGWIN__)
-
-#    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 "allocator.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)
-*/
-typedef struct {
-  thrd_start_t    func;
-  void           *arg;
-  kit_allocator_t alloc;
-} impl_thrd_param_t;
-
-static void *impl_thrd_routine(void *p) {
-  impl_thrd_param_t pack = *((impl_thrd_param_t *) p);
-  pack.alloc.deallocate(pack.alloc.state, 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_with_stack(thrd_t *thr, thrd_start_t func, void *arg,
-                           ptrdiff_t const require_stack_size) {
-  impl_thrd_param_t *pack;
-  assert(thr != NULL);
-  assert(require_stack_size == 0 ||
-         require_stack_size >= PTHREAD_STACK_MIN);
-  pthread_attr_t  attr;
-  pthread_attr_t *attr_p = NULL;
-  if (require_stack_size > 0) {
-    ptrdiff_t const page_size  = (ptrdiff_t) sysconf(_SC_PAGESIZE);
-    ptrdiff_t const delta      = require_stack_size % page_size;
-    ptrdiff_t const stack_size = delta == 0 ? require_stack_size
-                                            : require_stack_size +
-                                                  page_size - delta;
-    if (pthread_attr_init(&attr) != 0)
-      return thrd_nomem;
-    if (pthread_attr_setstacksize(&attr, (size_t) stack_size) != 0)
-      return thrd_wrong_stack_size;
-    attr_p = &attr;
-  }
-  kit_allocator_t alloc = kit_alloc_default();
-  pack                  = (impl_thrd_param_t *) alloc.allocate(
-      alloc.state, sizeof(impl_thrd_param_t));
-  if (!pack) {
-    if (attr_p)
-      pthread_attr_destroy(attr_p);
-    return thrd_nomem;
-  }
-  pack->func  = func;
-  pack->arg   = arg;
-  pack->alloc = alloc;
-  if (pthread_create(thr, attr_p, impl_thrd_routine, pack) != 0) {
-    alloc.deallocate(alloc.state, pack);
-    if (attr_p)
-      pthread_attr_destroy(attr_p);
-    return thrd_error;
-  }
-  if (attr_p)
-    pthread_attr_destroy(attr_p);
-  return thrd_success;
-}
-
-int thrd_create(thrd_t *thr, thrd_start_t func, void *arg) {
-  return thrd_create_with_stack(thr, func, arg, 0);
-}
-// 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();
-}
-
-#  endif
-#endif
diff --git a/source/kit/threads.win32.c b/source/kit/threads.win32.c
deleted file mode 100644
index 534b01c..0000000
--- a/source/kit/threads.win32.c
+++ /dev/null
@@ -1,433 +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_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 "allocator.h"
-#    include "threads.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*
-*/
-
-typedef struct {
-  thrd_start_t    func;
-  void           *arg;
-  thrd_t          thrd;
-  kit_allocator_t alloc;
-} impl_thrd_param_t;
-
-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) {
-  impl_thrd_param_t *pack_p = (impl_thrd_param_t *) p;
-  impl_thrd_param_t  pack;
-  int                code;
-  impl_current_thread.thrd              = pack_p->thrd;
-  impl_current_thread.handle_need_close = false;
-  memcpy(&pack, pack_p, sizeof(impl_thrd_param_t));
-  pack.alloc.deallocate(pack.alloc.state, 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;
-}
-
-/*------------------- 7.25.5 Thread functions -------------------*/
-// 7.25.5.1
-int thrd_create_with_stack(thrd_t *thr, thrd_start_t func, void *arg,
-                           ptrdiff_t const stack_size) {
-  impl_thrd_param_t *pack;
-  uintptr_t          handle;
-  assert(thr != NULL);
-  assert(stack_size >= 0 && stack_size < 0x100000000);
-  kit_allocator_t alloc = kit_alloc_default();
-  pack                  = (impl_thrd_param_t *) alloc.allocate(
-      alloc.state, (sizeof(impl_thrd_param_t)));
-  if (!pack)
-    return thrd_nomem;
-  pack->func  = func;
-  pack->arg   = arg;
-  pack->alloc = alloc;
-  handle      = _beginthreadex(NULL, (unsigned) stack_size,
-                               impl_thrd_routine, pack, CREATE_SUSPENDED,
-                               NULL);
-  if (handle == 0) {
-    alloc.deallocate(alloc.state, 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;
-}
-
-int thrd_create(thrd_t *thr, thrd_start_t func, void *arg) {
-  return thrd_create_with_stack(thr, func, arg, 0);
-}
-
-// 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();
-}
-
-#  endif
-#endif /* KIT_DISABLE_SYSTEM_THREADS */
diff --git a/source/kit/time.c b/source/kit/time.c
index bcb3819..108434f 100644
--- a/source/kit/time.c
+++ b/source/kit/time.c
@@ -1,39 +1,6 @@
-/*
- * 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
+#ifdef KIT_NEED_TIMESPEC_GET
 
 #  if defined(_WIN32) && !defined(__CYGWIN__)
 
@@ -43,11 +10,11 @@
 #    include <windows.h>
 
 int timespec_get(struct timespec *ts, int base) {
-/* difference between 1970 and 1601 */
+/*  difference between 1970 and 1601 */
 #    define _TIMESPEC_IMPL_UNIX_EPOCH_IN_TICKS 116444736000000000ull
-/* 1 tick is 100 nanoseconds */
+/*  1 tick is 100 nanoseconds */
 #    define _TIMESPEC_IMPL_TICKS_PER_SECONDS 10000000ull
-  if (!ts)
+  if (ts == NULL)
     return 0;
   if (base == TIME_UTC) {
     FILETIME       ft;
@@ -71,7 +38,7 @@ int timespec_get(struct timespec *ts, int base) {
 #  else
 
 int timespec_get(struct timespec *ts, int base) {
-  if (!ts)
+  if (ts == NULL)
     return 0;
   if (base == TIME_UTC) {
     clock_gettime(CLOCK_REALTIME, ts);
diff --git a/source/kit/time.h b/source/kit/time.h
index 0d995f4..9445912 100644
--- a/source/kit/time.h
+++ b/source/kit/time.h
@@ -1,17 +1,8 @@
-/*
- * 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
@@ -20,43 +11,19 @@
 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
+#ifdef KIT_NEED_STRUCT_TIMESPEC
 struct timespec {
-  time_t tv_sec;  // Seconds - >= 0
-  long   tv_nsec; // Nanoseconds - [0, 999999999]
+  time_t tv_sec;  /* Seconds      - >= 0 */
+  long   tv_nsec; /* Nanoseconds  - [0, 999999999] */
 };
 #endif
 
-/*-------------------------- functions --------------------------*/
-
-#if !defined(KIT_HAVE_TIMESPEC_GET)
-#  define KIT_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_TIMESPEC_GET_NEED_DECL_
-#endif
-
-#ifdef KIT_TIMESPEC_GET_NEED_DECL_
-/*-------------------- 7.25.7 Time functions --------------------*/
-// 7.25.6.1
+#ifdef KIT_NEED_TIMESPEC_GET
 int timespec_get(struct timespec *ts, int base);
-#  undef KIT_TIMESPEC_GET_NEED_DECL_
 #endif
 
 #ifdef __cplusplus
 }
 #endif
 
-#endif /* KIT_TIME_H */
+#endif
diff --git a/source/test/unittests/atomic.test.c b/source/test/unittests/atomic.test.c
index c909cb0..b8cf0ac 100644
--- a/source/test/unittests/atomic.test.c
+++ b/source/test/unittests/atomic.test.c
@@ -1,5 +1,5 @@
 #include "../../kit/atomic.h"
-#include "../../kit/threads.h"
+#include "../../kit/thread.h"
 
 #define KIT_TEST_FILE atomic
 #include "../../kit_test/test.h"
diff --git a/source/test/unittests/condition_variable.test.c b/source/test/unittests/condition_variable.test.c
index c855b0c..c5b0468 100644
--- a/source/test/unittests/condition_variable.test.c
+++ b/source/test/unittests/condition_variable.test.c
@@ -1,4 +1,5 @@
 #include "../../kit/condition_variable.h"
+#include "../../kit/thread.h"
 
 #define KIT_TEST_FILE condition_variable
 #include "../../kit_test/test.h"
diff --git a/source/test/unittests/mersenne_twister_64.test.c b/source/test/unittests/mersenne_twister_64.test.c
index b986c55..80d1c2c 100644
--- a/source/test/unittests/mersenne_twister_64.test.c
+++ b/source/test/unittests/mersenne_twister_64.test.c
@@ -1,4 +1,5 @@
 #include "../../kit/mersenne_twister_64.h"
+#include "../../kit/secure_random.h"
 
 #define KIT_TEST_FILE mersenne_twister_64
 #include "../../kit_test/test.h"
@@ -6,7 +7,8 @@
 enum { SIZE = 1000 };
 
 TEST("mt64 same seeds") {
-  uint64_t seed = mt64_seed();
+  uint64_t seed;
+  secure_random(sizeof seed, &seed);
 
   mt64_state_t foo, bar;
   mt64_init(&foo, seed);
diff --git a/source/test/unittests/mutex.test.c b/source/test/unittests/mutex.test.c
index a898557..e597e59 100644
--- a/source/test/unittests/mutex.test.c
+++ b/source/test/unittests/mutex.test.c
@@ -1,4 +1,5 @@
 #include "../../kit/mutex.h"
+#include "../../kit/thread.h"
 
 #define KIT_TEST_FILE mutex
 #include "../../kit_test/test.h"
diff --git a/source/test/unittests/secure_random.test.c b/source/test/unittests/secure_random.test.c
index eadae65..ec0a7b2 100644
--- a/source/test/unittests/secure_random.test.c
+++ b/source/test/unittests/secure_random.test.c
@@ -8,13 +8,9 @@ TEST("secure random") {
   int v[20];
   memset(v, 0, sizeof v);
 
-  secure_random_init();
-
   secure_random(40, v);
   secure_random(40, v + 10);
 
-  secure_random_cleanup();
-
   int repeats = 0;
 
   for (int i = 1; i < sizeof v / sizeof *v; i++)
diff --git a/source/test/unittests/thread.test.c b/source/test/unittests/thread.test.c
index 786e992..01198c2 100644
--- a/source/test/unittests/thread.test.c
+++ b/source/test/unittests/thread.test.c
@@ -1,4 +1,4 @@
-#include "../../kit/threads.h"
+#include "../../kit/thread.h"
 
 #define KIT_TEST_FILE thread
 #include "../../kit_test/test.h"
-- 
cgit v1.2.3