diff options
-rw-r--r-- | CMakeLists.txt | 4 | ||||
-rw-r--r-- | source/kit/CMakeLists.txt | 7 | ||||
-rw-r--r-- | source/kit/atomic.c | 42 | ||||
-rw-r--r-- | source/kit/atomic.h | 59 | ||||
-rw-r--r-- | source/kit/thread.c | 45 | ||||
-rw-r--r-- | source/kit/thread.h | 23 | ||||
-rw-r--r-- | source/test/unittests/CMakeLists.txt | 5 | ||||
-rw-r--r-- | source/test/unittests/atomic.test.c | 26 | ||||
-rw-r--r-- | source/test/unittests/thread.test.c | 18 |
9 files changed, 224 insertions, 5 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 8ec04fb..6903439 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,10 +50,12 @@ endif() enable_testing() if(KIT_ENABLE_TESTING) + find_package(Threads REQUIRED) + 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) - target_link_libraries(${KIT_TEST_SUITE} PRIVATE ${KIT_LIBRARY} ${KIT_TEST_LIBRARY}) + target_link_libraries(${KIT_TEST_SUITE} PRIVATE ${KIT_LIBRARY} ${KIT_TEST_LIBRARY} Threads::Threads) if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") target_compile_options( diff --git a/source/kit/CMakeLists.txt b/source/kit/CMakeLists.txt index c7bb204..21cfd1e 100644 --- a/source/kit/CMakeLists.txt +++ b/source/kit/CMakeLists.txt @@ -1,11 +1,14 @@ target_sources( ${KIT_LIBRARY} PRIVATE - input_buffer.c input_stream.c lower_bound.c string_ref.c - async_function.c allocator.c array_ref.c dynamic_array.c + atomic.c input_buffer.c input_stream.c lower_bound.c + thread.c string_ref.c async_function.c allocator.c array_ref.c + dynamic_array.c PUBLIC + $<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}/thread.h> $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/dynamic_array.h> $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/async_function.h> $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/input_stream.h> diff --git a/source/kit/atomic.c b/source/kit/atomic.c new file mode 100644 index 0000000..4321700 --- /dev/null +++ b/source/kit/atomic.c @@ -0,0 +1,42 @@ +#include "atomic.h" + +#ifdef _MSC_VEC +# include <intrin.h> + +# ifdef _WIN64 +# pragma intrinsic(_InterlockedExchange64) +# pragma intrinsic(_InterlockedExchangeAdd64) + +# define InterlockedExchange_ _InterlockedExchange64 +# define InterlockedExchangeAdd_ _InterlockedExchangeAdd64 +# else +# pragma intrinsic(_InterlockedExchange32) +# pragma intrinsic(_InterlockedExchangeAdd32) + +# define InterlockedExchange_ _InterlockedExchange32 +# define InterlockedExchangeAdd_ _InterlockedExchangeAdd32 +# endif + +void kit_atomic_store_explicit(volatile KIT_ATOMIC_VAR *var, + KIT_ATOMIC_VAR value, + int memory_order) { + InterlockedExchange_(var, value); +} + +KIT_ATOMIC_VAR kit_atomic_load_explicit(volatile KIT_ATOMIC_VAR *var, + int memory_order) { + return *var; +} + +KIT_ATOMIC_VAR kit_atomic_fetch_add_explicit( + volatile KIT_ATOMIC_VAR *var, KIT_ATOMIC_VAR value, + int memory_order) { + return InterlockedExchangeAdd_(var, value); +} + +KIT_ATOMIC_VAR kit_atomic_exchange_explicit( + volatile KIT_ATOMIC_VAR *var, KIT_ATOMIC_VAR value, + int memory_order) { + return InterlockedExchange_(var, value); +} +#endif diff --git a/source/kit/atomic.h b/source/kit/atomic.h new file mode 100644 index 0000000..b9242c2 --- /dev/null +++ b/source/kit/atomic.h @@ -0,0 +1,59 @@ +#ifndef KIT_ATOMIC_H +#define KIT_ATOMIC_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdint.h> + +#ifndef _MSC_VER +# include <stdatomic.h> + +# define KIT_ATOMIC(type_) _Atomic type_ +#else +enum { + memory_order_relaxed, + memory_order_consume, + memory_order_acquire, + memory_order_release, + memory_order_acq_rel, + memory_order_seq_cst +}; +# ifdef _WIN64 +# define KIT_ATOMIC_VAR uint64_t +# else +# define KIT_ATOMIC_VAR volatile uint32_t +# endif +# define KIT_ATOMIC(type_) volatile KIT_ATOMIC_VAR + +void kit_atomic_store_explicit(volatile KIT_ATOMIC_VAR *var, + KIT_ATOMIC_VAR value, + int memory_order); + +KIT_ATOMIC_VAR kit_atomic_load_explicit(volatile KIT_ATOMIC_VAR *var, + int memory_order); + +KIT_ATOMIC_VAR kit_atomic_fetch_add_explicit( + volatile KIT_ATOMIC_VAR *var, KIT_ATOMIC_VAR value, + int memory_order); + +KIT_ATOMIC_VAR kit_atomic_exchange_explicit( + volatile KIT_ATOMIC_VAR *var, KIT_ATOMIC_VAR value, + int memory_order); + +# define atomic_store_explicit kit_atomic_store_explicit +# define atomic_load_explicit kit_atomic_load_explicit +# define atomic_fetch_add_explicit kit_atomic_fetch_add_explicit +# define atomic_exchange_explicit kit_atomic_exchange_explicit +#endif + +#ifndef KIT_DISABLE_SHORT_NAMES +# define ATOMIC KIT_ATOMIC +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/kit/thread.c b/source/kit/thread.c new file mode 100644 index 0000000..71437eb --- /dev/null +++ b/source/kit/thread.c @@ -0,0 +1,45 @@ +#include "thread.h" + +#ifdef _MSC_VER +# include "atomic.h" + +# include <process.h> +# include <stdlib.h> + +typedef struct { + HANDLE thread; + kit_thread_routine routine; + void *user_data; + void *return_value; +} thread_data; + +DWORD __stdcall run_thread_(void *p) { + thread_data *data = (thread_data *) p; + data->return_value = data->routine(data->user_data); +} + +int pthread_create(pthread_t *new_thread, void *attrs, + void *(*routine)(void *), void *user_data) { + thread_data *data = (thread_data *) malloc(sizeof(thread_data)); + if (data == NULL) + return -1; + data->routine = routine; + data->user_data = user_data; + data->thread = CreateThread(NULL, 0, run_thread_, data, 0, NULL); + if (data->thread == NULL) + return -1; + if (new_thread != NULL) + *new_thread = data; + return 0; +} + +void *pthread_join(pthread_t thread, void *return_value) { + thread_data *data = (thread_data *) thread; + if (data == NULL || data->thread == NULL) + return (void *) 0; + WaitForSingleObject(data->thread, INFINITE); + void *return_value = data->return_value; + free(data); + return return_value; +} +#endif diff --git a/source/kit/thread.h b/source/kit/thread.h new file mode 100644 index 0000000..a1138ae --- /dev/null +++ b/source/kit/thread.h @@ -0,0 +1,23 @@ +#ifndef KIT_THREAD_H +#define KIT_THREAD_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _MSC_VER +# include "pthread.h" +#else +typedef void *pthread_t; + +int pthread_create(pthread_t *new_thread, void *attrs, + void *(*routine)(void *), void *user_data); + +void *pthread_join(pthread_t thread, void *return_value); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/test/unittests/CMakeLists.txt b/source/test/unittests/CMakeLists.txt index 674f442..853d896 100644 --- a/source/test/unittests/CMakeLists.txt +++ b/source/test/unittests/CMakeLists.txt @@ -2,5 +2,6 @@ target_sources( ${KIT_TEST_SUITE} PRIVATE async_function.test.c test_duration.test.c main.test.c - string_ref.test.c array_ref.test.c input_stream.test.c lower_bound.test.c - input_buffer.test.c dynamic_array.test.c) + string_ref.test.c atomic.test.c thread.test.c array_ref.test.c + input_stream.test.c lower_bound.test.c input_buffer.test.c + dynamic_array.test.c) diff --git a/source/test/unittests/atomic.test.c b/source/test/unittests/atomic.test.c new file mode 100644 index 0000000..9f1a10b --- /dev/null +++ b/source/test/unittests/atomic.test.c @@ -0,0 +1,26 @@ +#include "../../kit/atomic.h" + +#define KIT_TEST_FILE atomic +#include "../../kit_test/test.h" + +TEST("atomic store and load") { + ATOMIC(int) value; + atomic_store_explicit(&value, 20, memory_order_relaxed); + REQUIRE(atomic_load_explicit(&value, memory_order_relaxed) == 20); +} + +TEST("atomic exchange") { + ATOMIC(int) value; + atomic_store_explicit(&value, 20, memory_order_relaxed); + REQUIRE(atomic_exchange_explicit(&value, 42, + memory_order_relaxed) == 20); + REQUIRE(atomic_load_explicit(&value, memory_order_relaxed) == 42); +} + +TEST("atomic fetch add") { + ATOMIC(int) value; + atomic_store_explicit(&value, 20, memory_order_relaxed); + REQUIRE(atomic_fetch_add_explicit(&value, 22, + memory_order_relaxed) == 20); + REQUIRE(atomic_load_explicit(&value, memory_order_relaxed) == 42); +} diff --git a/source/test/unittests/thread.test.c b/source/test/unittests/thread.test.c new file mode 100644 index 0000000..fed564b --- /dev/null +++ b/source/test/unittests/thread.test.c @@ -0,0 +1,18 @@ +#include "../../kit/thread.h" + +#define KIT_TEST_FILE thread +#include "../../kit_test/test.h" + +static void *test_thread_fn(void *data) { + ptrdiff_t *value = (ptrdiff_t *) data; + return (void *) (*value + 20); +} + +TEST("run thread") { + pthread_t t; + ptrdiff_t value = 22; + pthread_create(&t, NULL, test_thread_fn, &value); + void *result; + pthread_join(t, &result); + REQUIRE((ptrdiff_t) result == 42); +} |