summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/kit/CMakeLists.txt1
-rw-r--r--source/kit/c11/CMakeLists.txt6
-rw-r--r--source/kit/c11/impl/CMakeLists.txt7
-rw-r--r--source/kit/c11/impl/threads_posix.c313
-rw-r--r--source/kit/c11/impl/threads_win32.c460
-rw-r--r--source/kit/c11/impl/threads_win32.h21
-rw-r--r--source/kit/c11/impl/threads_win32_tls_callback.cpp18
-rw-r--r--source/kit/c11/impl/time.c84
-rw-r--r--source/kit/c11/threads.h190
-rw-r--r--source/kit/c11/time.h62
-rw-r--r--source/kit/mersenne_twister_64.h2
-rw-r--r--source/test/unittests/c11_threads.c6
12 files changed, 1169 insertions, 1 deletions
diff --git a/source/kit/CMakeLists.txt b/source/kit/CMakeLists.txt
index 5891851..397b404 100644
--- a/source/kit/CMakeLists.txt
+++ b/source/kit/CMakeLists.txt
@@ -15,3 +15,4 @@ target_sources(
$<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
new file mode 100644
index 0000000..205b256
--- /dev/null
+++ b/source/kit/c11/CMakeLists.txt
@@ -0,0 +1,6 @@
+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
new file mode 100644
index 0000000..c64b895
--- /dev/null
+++ b/source/kit/c11/impl/CMakeLists.txt
@@ -0,0 +1,7 @@
+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
new file mode 100644
index 0000000..ebf411c
--- /dev/null
+++ b/source/kit/c11/impl/threads_posix.c
@@ -0,0 +1,313 @@
+/*
+ * 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
new file mode 100644
index 0000000..b94d20f
--- /dev/null
+++ b/source/kit/c11/impl/threads_win32.c
@@ -0,0 +1,460 @@
+/*
+ * 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
new file mode 100644
index 0000000..6adf5d2
--- /dev/null
+++ b/source/kit/c11/impl/threads_win32.h
@@ -0,0 +1,21 @@
+/*
+ * 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
new file mode 100644
index 0000000..27874c0
--- /dev/null
+++ b/source/kit/c11/impl/threads_win32_tls_callback.cpp
@@ -0,0 +1,18 @@
+/*
+ * 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
new file mode 100644
index 0000000..777b976
--- /dev/null
+++ b/source/kit/c11/impl/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 /* KIT_HAVE_TIMESPEC_GET */
diff --git a/source/kit/c11/threads.h b/source/kit/c11/threads.h
new file mode 100644
index 0000000..be6bc2d
--- /dev/null
+++ b/source/kit/c11/threads.h
@@ -0,0 +1,190 @@
+/*
+ * 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>
+
+#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
+# define thread_local _Thread_local
+# 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
new file mode 100644
index 0000000..7467a33
--- /dev/null
+++ b/source/kit/c11/time.h
@@ -0,0 +1,62 @@
+/*
+ * 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.h b/source/kit/mersenne_twister_64.h
index 605a25f..416ef27 100644
--- a/source/kit/mersenne_twister_64.h
+++ b/source/kit/mersenne_twister_64.h
@@ -23,7 +23,7 @@ uint64_t kit_mt64_generate(kit_mt64_state_t *state);
uint64_t kit_mt64_seed();
-#ifndef LAPLACE_DISABLE_SHORT_NAMES
+#ifndef KIT_DISABLE_SHORT_NAMES
# define mt64_state_t kit_mt64_state_t
# define mt64_init kit_mt64_init
# define mt64_generate kit_mt64_generate
diff --git a/source/test/unittests/c11_threads.c b/source/test/unittests/c11_threads.c
new file mode 100644
index 0000000..cd50846
--- /dev/null
+++ b/source/test/unittests/c11_threads.c
@@ -0,0 +1,6 @@
+#include "../../kit/c11/threads.h"
+
+#define KIT_TEST_FILE atomic
+#include "../../kit_test/test.h"
+
+TEST("c11 threads") { }