From 37236794304626096684f020e5f2f02d12cb4488 Mon Sep 17 00:00:00 2001 From: Mitya Selivanov <0x7fffff@guattari.ru> Date: Tue, 16 Aug 2022 05:08:47 +0400 Subject: test --- CMakeLists.txt | 237 +++++++---- gen_cmake.py | 2 +- source/kit/CMakeLists.txt | 1 + source/kit/c11/CMakeLists.txt | 6 + source/kit/c11/impl/CMakeLists.txt | 7 + source/kit/c11/impl/threads_posix.c | 313 ++++++++++++++ source/kit/c11/impl/threads_win32.c | 460 +++++++++++++++++++++ source/kit/c11/impl/threads_win32.h | 21 + source/kit/c11/impl/threads_win32_tls_callback.cpp | 18 + source/kit/c11/impl/time.c | 84 ++++ source/kit/c11/threads.h | 190 +++++++++ source/kit/c11/time.h | 62 +++ source/kit/mersenne_twister_64.h | 2 +- source/test/unittests/c11_threads.c | 6 + 14 files changed, 1328 insertions(+), 81 deletions(-) create mode 100644 source/kit/c11/CMakeLists.txt create mode 100644 source/kit/c11/impl/CMakeLists.txt create mode 100644 source/kit/c11/impl/threads_posix.c create mode 100644 source/kit/c11/impl/threads_win32.c create mode 100644 source/kit/c11/impl/threads_win32.h create mode 100644 source/kit/c11/impl/threads_win32_tls_callback.cpp create mode 100644 source/kit/c11/impl/time.c create mode 100644 source/kit/c11/threads.h create mode 100644 source/kit/c11/time.h create mode 100644 source/test/unittests/c11_threads.c diff --git a/CMakeLists.txt b/CMakeLists.txt index f00e62e..b96abd8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,5 @@ cmake_minimum_required(VERSION 3.16) -option(KIT_ENABLE_LIBRARY "Enable library" ON) option(KIT_ENABLE_TESTING "Enable testing" ON) option(KIT_DISABLE_SYSTEM_MALLOC "Disable system memory allocator" OFF) @@ -22,36 +21,118 @@ project( DESCRIPTION "A collection of C libraries" LANGUAGES C CXX) -if(KIT_ENABLE_LIBRARY OR KIT_ENABLE_TESTING) - add_library(${KIT_LIBRARY} STATIC) - add_library(${KIT_PROJECT}::${KIT_LIBRARY} ALIAS ${KIT_LIBRARY}) - target_include_directories( - ${KIT_LIBRARY} INTERFACE - $) - target_compile_features(${KIT_LIBRARY} PUBLIC c_std_11) +add_library(${KIT_LIBRARY} STATIC) +add_library(${KIT_PROJECT}::${KIT_LIBRARY} ALIAS ${KIT_LIBRARY}) +target_include_directories( + ${KIT_LIBRARY} INTERFACE + $) +target_compile_features(${KIT_LIBRARY} PUBLIC c_std_11) + +include(CheckCSourceRuns) + +check_c_source_runs( + " + #include + int main() { + struct timespec t; + return 0; + } + " + KIT_HAVE_STRUCT_TIMESPEC +) + +check_c_source_runs( + " + #include + int main() { + struct timespec t; + timespec_get(&t, TIME_UTC); + return 0; + } + " + KIT_HAVE_TIMESPEC_GET +) + +check_c_source_runs( + " + #include + int main() { + return 0; + } + " + KIT_HAVE_PTHREAD +) + +check_c_source_runs( + " + #include + int main() { + return 0; + } + " + KIT_HAVE_WINDOWS +) + +check_c_source_runs( + " + #include + int main() { + void *p = malloc(10); + free(p); + return 0; + } + " + KIT_HAVE_MALLOC +) + +if(NOT KIT_HAVE_PTHREAD AND NOT KIT_HAVE_WINDOWS) + set(KIT_DISABLE_SYSTEM_THREADS ON) +endif() - if(KIT_DISABLE_SYSTEM_MALLOC) - target_compile_definitions(${KIT_LIBRARY} PUBLIC KIT_DISABLE_SYSTEM_MALLOC) - endif() +if(NOT KIT_HAVE_MALLOC) + set(KIT_DISABLE_SYSTEM_MALLOC ON) +endif() - if(KIT_DISABLE_SYSTEM_THREADS) - target_compile_definitions(${KIT_LIBRARY} PUBLIC KIT_DISABLE_SYSTEM_THREADS) - endif() +if(KIT_HAVE_STRUCT_TIMESPEC) + target_compile_definitions(${KIT_LIBRARY} PUBLIC KIT_HAVE_STRUCT_TIMESPEC) +endif() - add_library(${KIT_TEST_LIBRARY} STATIC) - add_library(${KIT_PROJECT}::${KIT_TEST_LIBRARY} ALIAS ${KIT_TEST_LIBRARY}) - target_include_directories( - ${KIT_TEST_LIBRARY} INTERFACE - $) - target_compile_features(${KIT_TEST_LIBRARY} PUBLIC c_std_11) - target_compile_features(${KIT_TEST_LIBRARY} PUBLIC cxx_std_11) +if(KIT_HAVE_TIMESPEC_GET) + target_compile_definitions(${KIT_LIBRARY} PUBLIC KIT_HAVE_TIMESPEC_GET) endif() -enable_testing() +if(KIT_HAVE_PTHREAD) + target_compile_definitions(${KIT_LIBRARY} PUBLIC KIT_HAVE_PTHREAD) +endif() -if(KIT_ENABLE_TESTING) +if(KIT_HAVE_WINDOWS) + target_compile_definitions(${KIT_LIBRARY} PUBLIC KIT_HAVE_WINDOWS) +endif() + +if(KIT_DISABLE_SYSTEM_MALLOC) + target_compile_definitions(${KIT_LIBRARY} PUBLIC KIT_DISABLE_SYSTEM_MALLOC) +endif() + +if(KIT_DISABLE_SYSTEM_THREADS) + target_compile_definitions(${KIT_LIBRARY} PUBLIC KIT_DISABLE_SYSTEM_THREADS) +endif() + +if(NOT KIT_DISABLE_SYSTEM_THREADS) find_package(Threads REQUIRED) + target_link_libraries(${KIT_LIBRARY} PUBLIC Threads::Threads) +endif() + +add_library(${KIT_TEST_LIBRARY} STATIC) +add_library(${KIT_PROJECT}::${KIT_TEST_LIBRARY} ALIAS ${KIT_TEST_LIBRARY}) +target_include_directories( + ${KIT_TEST_LIBRARY} INTERFACE + $) +target_compile_features(${KIT_TEST_LIBRARY} PUBLIC c_std_11) +target_compile_features(${KIT_TEST_LIBRARY} PUBLIC cxx_std_11) +enable_testing() + +if(KIT_ENABLE_TESTING) add_executable(${KIT_TEST_SUITE}) add_executable(${KIT_PROJECT}::${KIT_TEST_SUITE} ALIAS ${KIT_TEST_SUITE}) target_compile_features(${KIT_TEST_SUITE} PRIVATE c_std_11) @@ -79,63 +160,61 @@ endif() add_subdirectory(source) -if(KIT_ENABLE_LIBRARY) - include(GNUInstallDirs) - - file( - GLOB_RECURSE headers_ - RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/source/kit - ${CMAKE_CURRENT_SOURCE_DIR}/source/kit/*.h) - - foreach(path_ ${headers_}) - get_filename_component(dir_ "${path_}" DIRECTORY) - - install( - FILES ${CMAKE_CURRENT_SOURCE_DIR}/source/kit/${path_} - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/kit/${dir_}) - endforeach() - - unset(headers_) - unset(path_) - unset(dir_) - - install( - TARGETS ${KIT_LIBRARY} ${KIT_TEST_LIBRARY} - EXPORT ${KIT_TARGETS} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) - - install( - EXPORT ${KIT_TARGETS} - FILE ${KIT_TARGETS}.cmake - NAMESPACE ${PROJECT_NAME}:: - DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}") - - export( - EXPORT ${KIT_TARGETS} - FILE "${CMAKE_CURRENT_BINARY_DIR}/cmake/${KIT_TARGETS}.cmake" - NAMESPACE ${PROJECT_NAME}::) +include(GNUInstallDirs) - include(CMakePackageConfigHelpers) +file( + GLOB_RECURSE headers_ + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/source/kit + ${CMAKE_CURRENT_SOURCE_DIR}/source/kit/*.h) - string(TOLOWER ${PROJECT_NAME} project_lower_) - - configure_package_config_file( - ${CMAKE_CURRENT_SOURCE_DIR}/config.cmake.in - "${CMAKE_CURRENT_BINARY_DIR}/${project_lower_}-config.cmake" - INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}) - - write_basic_package_version_file( - "${CMAKE_CURRENT_BINARY_DIR}/${project_lower_}-config-version.cmake" - VERSION "${PROJECT_VERSION}" - COMPATIBILITY AnyNewerVersion) +foreach(path_ ${headers_}) + get_filename_component(dir_ "${path_}" DIRECTORY) install( - FILES - "${CMAKE_CURRENT_BINARY_DIR}/${project_lower_}-config.cmake" - "${CMAKE_CURRENT_BINARY_DIR}/${project_lower_}-config-version.cmake" - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}) - - unset(project_lower_) -endif() + FILES ${CMAKE_CURRENT_SOURCE_DIR}/source/kit/${path_} + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/kit/${dir_}) +endforeach() + +unset(headers_) +unset(path_) +unset(dir_) + +install( + TARGETS ${KIT_LIBRARY} ${KIT_TEST_LIBRARY} + EXPORT ${KIT_TARGETS} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + +install( + EXPORT ${KIT_TARGETS} + FILE ${KIT_TARGETS}.cmake + NAMESPACE ${PROJECT_NAME}:: + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}") + +export( + EXPORT ${KIT_TARGETS} + FILE "${CMAKE_CURRENT_BINARY_DIR}/cmake/${KIT_TARGETS}.cmake" + NAMESPACE ${PROJECT_NAME}::) + +include(CMakePackageConfigHelpers) + +string(TOLOWER ${PROJECT_NAME} project_lower_) + +configure_package_config_file( + ${CMAKE_CURRENT_SOURCE_DIR}/config.cmake.in + "${CMAKE_CURRENT_BINARY_DIR}/${project_lower_}-config.cmake" + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}) + +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/${project_lower_}-config-version.cmake" + VERSION "${PROJECT_VERSION}" + COMPATIBILITY AnyNewerVersion) + +install( + FILES + "${CMAKE_CURRENT_BINARY_DIR}/${project_lower_}-config.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/${project_lower_}-config-version.cmake" + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}) + +unset(project_lower_) \ No newline at end of file diff --git a/gen_cmake.py b/gen_cmake.py index 3d1a520..ec3a176 100755 --- a/gen_cmake.py +++ b/gen_cmake.py @@ -39,7 +39,7 @@ def print_list(s: list, offset: int): def print_sources(folder: str, target_name: str): buf = '' - srcs = get_files(folder, '*.c') + srcs = get_files(folder, '*.c') + get_files(folder, '*.cpp') hdrs = get_files(folder, '*.h') if len(srcs) > 0 or len(hdrs) > 0: buf += 'target_sources(\n ' + target_name 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( $ $ $) +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 + $ + $) +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 + $) 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 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 +# include +# include +# include +# include /* for intptr_t */ +# include +# include + +# 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 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 +# include +# include +# include // MSVCRT +# include +# include + +# include "../threads.h" + +# include "threads_win32.h" + +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN 1 +# endif +# include + +/* +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) ¶m, 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 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 + +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 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 +#include +#include + +#if defined(_WIN32) && !defined(__CYGWIN__) +# include /* close */ +# include /* _exit */ +#elif defined(KIT_HAVE_PTHREAD) +# include +# include /* 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 emulation library + */ + +#ifndef KIT_C11_TIME_H_INCLUDED_ +#define KIT_C11_TIME_H_INCLUDED_ + +#include + +/*---------------------------- 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") { } -- cgit v1.2.3