diff options
author | Mitya Selivanov <0x7fffff@guattari.ru> | 2022-08-18 06:50:13 +0400 |
---|---|---|
committer | Mitya Selivanov <0x7fffff@guattari.ru> | 2022-08-18 06:50:13 +0400 |
commit | 5f80c8b54d011580383810597aa68565ddfb8f3c (patch) | |
tree | 3bf431e61e580a68de55840fa016dbfa0767d4e9 | |
parent | ac4face2d7d6d2b033874d3e6e24d2133c96132f (diff) | |
download | kit-5f80c8b54d011580383810597aa68565ddfb8f3c.zip |
[test] thread, mutex, condition variable
19 files changed, 382 insertions, 160 deletions
@@ -1,3 +1,5 @@ +Copyright (c) 2012 yohhoy +Copyright (c) 2022 Yonggang Luo Copyright (c) 2022 Mitya Selivanov Permission is hereby granted, free of charge, to any person obtaining a copy @@ -1,7 +1,16 @@ # kit + - Unit-testing -- Async function - Lower bound - Dynamic array - Input buffer - Random number generation +- Async function +- Atomic +- Condition variable +- Mutual exclusion +- Thread + +Condition variables, mutual exclusions and threads implementation was forked from [Mesa][mesa_link] source code. + +[mesa_link]: https://gitlab.freedesktop.org/mesa/mesa diff --git a/source/kit/CMakeLists.txt b/source/kit/CMakeLists.txt index 397b404..86c39dc 100644 --- a/source/kit/CMakeLists.txt +++ b/source/kit/CMakeLists.txt @@ -1,18 +1,21 @@ target_sources( ${KIT_LIBRARY} PRIVATE - atomic.c input_buffer.c input_stream.c lower_bound.c - string_ref.c async_function.c allocator.c array_ref.c dynamic_array.c - mersenne_twister_64.c + atomic.c input_buffer.c threads.win32.c time.c + threads.posix.c input_stream.c lower_bound.c string_ref.c + async_function.c allocator.c array_ref.c dynamic_array.c + mersenne_twister_64.c threads_tls_callback.win32.cpp PUBLIC + $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/time.h> $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/atomic.h> $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/allocator.h> $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/string_ref.h> + $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/threads.h> $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/dynamic_array.h> $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/async_function.h> + $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/threads.win32.h> $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/input_stream.h> $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/input_buffer.h> $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/lower_bound.h> $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/array_ref.h> $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/mersenne_twister_64.h>) -add_subdirectory(c11) diff --git a/source/kit/c11/CMakeLists.txt b/source/kit/c11/CMakeLists.txt deleted file mode 100644 index 205b256..0000000 --- a/source/kit/c11/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -target_sources( - ${KIT_LIBRARY} - PUBLIC - $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/time.h> - $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/threads.h>) -add_subdirectory(impl) diff --git a/source/kit/c11/impl/CMakeLists.txt b/source/kit/c11/impl/CMakeLists.txt deleted file mode 100644 index c64b895..0000000 --- a/source/kit/c11/impl/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -target_sources( - ${KIT_LIBRARY} - PRIVATE - time.c threads_posix.c threads_win32.c - threads_win32_tls_callback.cpp - PUBLIC - $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/threads_win32.h>) diff --git a/source/kit/c11/impl/threads_win32.h b/source/kit/c11/impl/threads_win32.h deleted file mode 100644 index 6adf5d2..0000000 --- a/source/kit/c11/impl/threads_win32.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2022 Yonggang Luo - * SPDX-License-Identifier: MIT - */ - -#ifndef KIT_C11_IMPL_THREADS_WIN32_H_ -#define KIT_C11_IMPL_THREADS_WIN32_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef KIT_HAVE_WINDOWS -void __threads_win32_tls_callback(void); -#endif - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/source/kit/mersenne_twister_64.c b/source/kit/mersenne_twister_64.c index 4be4313..9ab39d3 100644 --- a/source/kit/mersenne_twister_64.c +++ b/source/kit/mersenne_twister_64.c @@ -1,6 +1,6 @@ #include "mersenne_twister_64.h" -#include <time.h> +#include "time.h" void kit_mt64_init(kit_mt64_state_t *state, uint64_t seed) { state->mt[0] = seed; diff --git a/source/kit/c11/threads.h b/source/kit/threads.h index 0b73183..d98978d 100644 --- a/source/kit/c11/threads.h +++ b/source/kit/threads.h @@ -31,71 +31,72 @@ * DEALINGS IN THE SOFTWARE. */ -#ifndef KIT_C11_THREADS_H_INCLUDED_ -#define KIT_C11_THREADS_H_INCLUDED_ +#ifndef KIT_THREADS_H +#define KIT_THREADS_H -#include "time.h" +#ifndef KIT_DISABLE_SYSTEM_THREADS +# include "time.h" -#include <errno.h> -#include <limits.h> -#include <stdlib.h> +# include <errno.h> +# include <limits.h> +# include <stdlib.h> -#ifdef _MSC_VER -# define _Noreturn __declspec(noreturn) -#endif +# ifdef _MSC_VER +# define _Noreturn __declspec(noreturn) +# endif -#if defined(_WIN32) && !defined(__CYGWIN__) -# include <io.h> /* close */ -# include <process.h> /* _exit */ -#elif defined(KIT_HAVE_PTHREAD) -# include <pthread.h> -# include <unistd.h> /* close, _exit */ -#else -# error Not supported on this platform. -#endif +# 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) +# ifndef _Thread_local +# if defined(__cplusplus) /* C++11 doesn't need `_Thread_local` keyword or macro */ -# elif !defined(__STDC_NO_THREADS__) +# 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 +# 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 -#endif -#if !defined(__cplusplus) +# 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 +# ifndef thread_local +# if _MSC_VER +# define thread_local __declspec(thread) +# else +# define thread_local _Thread_local +# endif # endif # endif -#endif -#ifdef __cplusplus +# ifdef __cplusplus extern "C" { -#endif +# endif /*---------------------------- types ----------------------------*/ typedef void (*tss_dtor_t)(void *); typedef int (*thrd_start_t)(void *); -#if defined(_WIN32) && !defined(__CYGWIN__) +# if defined(_WIN32) && !defined(__CYGWIN__) typedef struct { void *Ptr; } cnd_t; @@ -117,29 +118,29 @@ 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) +# 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 +# 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 -# define TSS_DTOR_ITERATIONS \ - 1 // assume TSS dtor MAY be called at least once. +# error Not supported on this platform. # endif -#else -# error Not supported on this platform. -#endif /*-------------------- enumeration constants --------------------*/ enum { @@ -177,11 +178,11 @@ 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) +# if defined(__cplusplus) [[ noreturn ]] -#else +# else _Noreturn -#endif +# endif void thrd_exit(int); int thrd_join(thrd_t, int *); int thrd_sleep(const struct timespec *, struct timespec *); @@ -191,8 +192,10 @@ void tss_delete(tss_t); void *tss_get(tss_t); int tss_set(tss_t, void *); -#ifdef __cplusplus +# ifdef __cplusplus } -#endif +# endif + +#endif /* KIT_DISABLE_SYSTEM_THREADS */ -#endif /* KIT_C11_THREADS_H_INCLUDED_ */ +#endif /* KIT_THREADS_H */ diff --git a/source/kit/c11/impl/threads_posix.c b/source/kit/threads.posix.c index ebf411c..20ef1ae 100644 --- a/source/kit/c11/impl/threads_posix.c +++ b/source/kit/threads.posix.c @@ -29,17 +29,18 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ -#ifndef KIT_HAVE_WINDOWS +#ifndef KIT_DISABLE_SYSTEM_THREADS +# ifdef KIT_HAVE_PTHREAD -# include <assert.h> -# include <errno.h> -# include <limits.h> -# include <sched.h> -# include <stdint.h> /* for intptr_t */ -# include <stdlib.h> -# include <unistd.h> +# include <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" +# include "threads.h" /* Configuration macro: @@ -48,10 +49,10 @@ Configuration macro: 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 +# if !defined(__CYGWIN__) && !defined(__APPLE__) && \ + !defined(__NetBSD__) +# define EMULATED_THREADS_USE_NATIVE_TIMEDLOCK +# endif /*---------------------------- types ----------------------------*/ @@ -149,7 +150,7 @@ void mtx_destroy(mtx_t *mtx) { * 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__) +# if defined(HAVE_FUNC_ATTRIBUTE_WEAK) && !defined(__CYGWIN__) __attribute__((weak)) int pthread_mutexattr_init( pthread_mutexattr_t *attr); @@ -158,7 +159,7 @@ __attribute__((weak)) int pthread_mutexattr_settype( __attribute__((weak)) int pthread_mutexattr_destroy( pthread_mutexattr_t *attr); -# endif +# endif // 7.25.4.2 int mtx_init(mtx_t *mtx, int type) { @@ -193,13 +194,13 @@ int mtx_timedlock(mtx_t *mtx, const struct timespec *ts) { assert(ts != NULL); { -# ifdef EMULATED_THREADS_USE_NATIVE_TIMEDLOCK +# 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 +# else time_t expire = time(NULL); expire += ts->tv_sec; while (mtx_trylock(mtx) != thrd_success) { @@ -210,7 +211,7 @@ int mtx_timedlock(mtx_t *mtx, const struct timespec *ts) { thrd_yield(); } return thrd_success; -# endif +# endif } } @@ -310,4 +311,5 @@ int tss_set(tss_t key, void *val) { : thrd_error; } +# endif #endif diff --git a/source/kit/c11/impl/threads_win32.c b/source/kit/threads.win32.c index b94d20f..d425a27 100644 --- a/source/kit/c11/impl/threads_win32.c +++ b/source/kit/threads.win32.c @@ -29,23 +29,24 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ -#ifdef KIT_HAVE_WINDOWS +#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 <assert.h> +# include <errno.h> +# include <limits.h> +# include <process.h> // MSVCRT +# include <stdbool.h> +# include <stdlib.h> -# include "../threads.h" +# include "threads.h" -# include "threads_win32.h" +# include "threads_win32.h" -# ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN 1 -# endif -# include <windows.h> +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN 1 +# endif +# include <windows.h> /* Configuration macro: @@ -59,20 +60,20 @@ Configuration macro: Max registerable TSS dtor number. */ -# if _WIN32_WINNT >= 0x0600 +# if _WIN32_WINNT >= 0x0600 // Prefer native WindowsAPI on newer environment. -# if !defined(__MINGW32__) -# define EMULATED_THREADS_USE_NATIVE_CALL_ONCE +# if !defined(__MINGW32__) +# define EMULATED_THREADS_USE_NATIVE_CALL_ONCE +# endif # endif -# endif -# define EMULATED_THREADS_TSS_DTOR_SLOTNUM \ - 64 // see TLS_MINIMUM_AVAILABLE +# 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 +# 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"); @@ -131,7 +132,7 @@ static DWORD impl_abs2relmsec(const struct timespec *abs_time) { return rel_ms; } -# ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE +# ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE struct impl_call_once_param { void (*func)(void); }; @@ -145,7 +146,7 @@ static BOOL CALLBACK impl_call_once_callback(PINIT_ONCE InitOnce, ((void) Context); // suppress warning return TRUE; } -# endif // ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE +# endif // ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE static struct impl_tss_dtor_entry { tss_t key; @@ -180,14 +181,14 @@ static void impl_tss_dtor_invoke(void) { // 7.25.2.1 void call_once(once_flag *flag, void (*func)(void)) { assert(flag && func); -# ifdef EMULATED_THREADS_USE_NATIVE_CALL_ONCE +# 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 +# else if (InterlockedCompareExchangePointer( (PVOID volatile *) &flag->status, (PVOID) 1, (PVOID) 0) == 0) { @@ -200,7 +201,7 @@ void call_once(once_flag *flag, void (*func)(void)) { thrd_yield(); } } -# endif +# endif } /*------------- 7.25.3 Condition variable functions -------------*/ @@ -457,4 +458,5 @@ int tss_set(tss_t key, void *val) { return TlsSetValue(key, val) ? thrd_success : thrd_error; } -#endif +# endif +#endif /* KIT_DISABLE_SYSTEM_THREADS */ diff --git a/source/kit/threads.win32.h b/source/kit/threads.win32.h new file mode 100644 index 0000000..e8e9abf --- /dev/null +++ b/source/kit/threads.win32.h @@ -0,0 +1,25 @@ +/* + * Copyright 2022 Yonggang Luo + * SPDX-License-Identifier: MIT + */ + +#ifndef KIT_THREADS_WIN32_H +#define KIT_THREADS_WIN32_H + +#ifndef KIT_DISABLE_SYSTEM_THREADS + +# ifdef __cplusplus +extern "C" { +# endif + +# if defined(_WIN32) && !defined(__CYGWIN__) +void __threads_win32_tls_callback(void); +# endif + +# ifdef __cplusplus +} +# endif + +#endif + +#endif diff --git a/source/kit/c11/impl/threads_win32_tls_callback.cpp b/source/kit/threads_tls_callback.win32.cpp index 27874c0..3cc3cc6 100644 --- a/source/kit/c11/impl/threads_win32_tls_callback.cpp +++ b/source/kit/threads_tls_callback.win32.cpp @@ -3,9 +3,10 @@ * SPDX-License-Identifier: MIT */ -#include "threads_win32.h" +#ifndef KIT_DISABLE_SYSTEM_THREADS +# if defined(_WIN32) && !defined(__CYGWIN__) -#ifdef KIT_HAVE_WINDOWS +# include "threads.win32.h" struct tls_callback { tls_callback() { } @@ -13,6 +14,8 @@ struct tls_callback { __threads_win32_tls_callback(); } }; + static thread_local tls_callback tls_callback_instance; +# endif #endif diff --git a/source/kit/c11/impl/time.c b/source/kit/time.c index 777b976..bcb3819 100644 --- a/source/kit/c11/impl/time.c +++ b/source/kit/time.c @@ -81,4 +81,4 @@ int timespec_get(struct timespec *ts, int base) { } # endif -#endif /* KIT_HAVE_TIMESPEC_GET */ +#endif diff --git a/source/kit/c11/time.h b/source/kit/time.h index 7467a33..dbf8883 100644 --- a/source/kit/c11/time.h +++ b/source/kit/time.h @@ -5,8 +5,8 @@ * C11 <time.h> emulation library */ -#ifndef KIT_C11_TIME_H_INCLUDED_ -#define KIT_C11_TIME_H_INCLUDED_ +#ifndef KIT_TIME_H +#define KIT_TIME_H #include <time.h> @@ -59,4 +59,4 @@ int timespec_get(struct timespec *ts, int base); } #endif -#endif /* KIT_C11_TIME_H_INCLUDED_ */ +#endif /* KIT_TIME_H */ diff --git a/source/test/unittests/CMakeLists.txt b/source/test/unittests/CMakeLists.txt index 5ec950d..e64628d 100644 --- a/source/test/unittests/CMakeLists.txt +++ b/source/test/unittests/CMakeLists.txt @@ -1,7 +1,8 @@ target_sources( ${KIT_TEST_SUITE} PRIVATE - async_function.test.c test_duration.test.c main.test.c - string_ref.test.c atomic.test.c array_ref.test.c input_stream.test.c - lower_bound.test.c mersenne_twister_64.test.c input_buffer.test.c + async_function.test.c mutex.test.c test_duration.test.c + main.test.c string_ref.test.c atomic.test.c thread.test.c + array_ref.test.c input_stream.test.c lower_bound.test.c + condition_variable.test.c mersenne_twister_64.test.c input_buffer.test.c dynamic_array.test.c) diff --git a/source/test/unittests/c11_threads.c b/source/test/unittests/c11_threads.c deleted file mode 100644 index cd50846..0000000 --- a/source/test/unittests/c11_threads.c +++ /dev/null @@ -1,6 +0,0 @@ -#include "../../kit/c11/threads.h" - -#define KIT_TEST_FILE atomic -#include "../../kit_test/test.h" - -TEST("c11 threads") { } diff --git a/source/test/unittests/condition_variable.test.c b/source/test/unittests/condition_variable.test.c new file mode 100644 index 0000000..c9d818a --- /dev/null +++ b/source/test/unittests/condition_variable.test.c @@ -0,0 +1,60 @@ +#include "../../kit/threads.h" + +#define KIT_TEST_FILE condition_variable +#include "../../kit_test/test.h" + +typedef struct { + mtx_t m; + cnd_t send; + cnd_t receive; + int value; +} test_data_t; + +static int test_run(void *p) { + test_data_t *data = (test_data_t *) p; + + mtx_lock(&data->m); + + data->value = 20; + mtx_unlock(&data->m); + cnd_broadcast(&data->send); + + cnd_wait(&data->receive, &data->m); + + data->value = 22; + cnd_broadcast(&data->send); + + mtx_unlock(&data->m); + + return 0; +} + +TEST("condition variable") { + test_data_t data; + REQUIRE(mtx_init(&data.m, mtx_plain) == thrd_success); + REQUIRE(cnd_init(&data.send) == thrd_success); + REQUIRE(cnd_init(&data.receive) == thrd_success); + data.value = 0; + + thrd_t t; + REQUIRE(thrd_create(&t, test_run, &data) == thrd_success); + + REQUIRE(mtx_lock(&data.m) == thrd_success); + + REQUIRE(cnd_wait(&data.send, &data.m) == thrd_success); + int x = data.value; + + REQUIRE(cnd_broadcast(&data.receive) == thrd_success); + + REQUIRE(cnd_wait(&data.send, &data.m) == thrd_success); + x += data.value; + + REQUIRE(mtx_unlock(&data.m) == thrd_success); + REQUIRE(thrd_join(t, NULL) == thrd_success); + + mtx_destroy(&data.m); + cnd_destroy(&data.send); + cnd_destroy(&data.receive); + + REQUIRE(x == 42); +} diff --git a/source/test/unittests/mutex.test.c b/source/test/unittests/mutex.test.c new file mode 100644 index 0000000..c0153e7 --- /dev/null +++ b/source/test/unittests/mutex.test.c @@ -0,0 +1,74 @@ +#include "../../kit/threads.h" + +#define KIT_TEST_FILE mutex +#include "../../kit_test/test.h" + +enum { THREAD_COUNT = 200 }; + +typedef struct { + mtx_t lock; + int value; +} test_data_t; + +static int test_run(void *data) { + test_data_t *x = (test_data_t *) data; + for (int i = 0; i < 1000; i++) { + mtx_lock(&x->lock); + + x->value += i; + thrd_yield(); + x->value -= i + 42; + thrd_yield(); + x->value += i + 20; + thrd_yield(); + x->value += 22 - i; + + mtx_unlock(&x->lock); + } + return 0; +} + +int test_lock_for_2_sec(void *data) { + mtx_t *m = (mtx_t *) data; + mtx_lock(m); + + struct timespec sec = { .tv_sec = 2, .tv_nsec = 0 }; + thrd_sleep(&sec, NULL); + + mtx_unlock(m); +} + +TEST("mutex lock") { + test_data_t data; + thrd_t pool[THREAD_COUNT]; + data.value = 42; + REQUIRE(mtx_init(&data.lock, mtx_plain) == thrd_success); + + for (ptrdiff_t i = 0; i < THREAD_COUNT; i++) + thrd_create(pool + i, test_run, &data); + for (ptrdiff_t i = 0; i < THREAD_COUNT; i++) + thrd_join(pool[i], NULL); + + mtx_destroy(&data.lock); + REQUIRE(data.value == 42); +} + +TEST("mutex try lock") { + mtx_t m; + REQUIRE(mtx_init(&m, mtx_plain) == thrd_success); + + thrd_t t; + REQUIRE(thrd_create(&t, test_lock_for_2_sec, &m) == thrd_success); + + struct timespec sec = { .tv_sec = 1, .tv_nsec = 0 }; + REQUIRE(thrd_sleep(&sec, NULL) == thrd_success); + + REQUIRE(mtx_trylock(&m) == thrd_busy); + + REQUIRE(thrd_join(t, NULL) == thrd_success); + + REQUIRE(mtx_trylock(&m) == thrd_success); + REQUIRE(mtx_unlock(&m) == thrd_success); + + mtx_destroy(&m); +} diff --git a/source/test/unittests/thread.test.c b/source/test/unittests/thread.test.c new file mode 100644 index 0000000..44e51b5 --- /dev/null +++ b/source/test/unittests/thread.test.c @@ -0,0 +1,78 @@ +#include "../../kit/threads.h" + +#define KIT_TEST_FILE thread +#include "../../kit_test/test.h" + +static int test_nothing(void *) { } + +static int test_run(void *data) { + int *n = (int *) data; + return *n + 20; +} + +static int test_exit(void *data) { + int *n = (int *) data; + + *n = 1; + thrd_exit(3); + *n = 2; + return 4; +} + +static int test_yield(void *data) { + thrd_yield(); +} + +static int test_sleep(void *data) { + struct timespec t = { .tv_sec = 0, .tv_nsec = 10000000 }; + thrd_sleep(&t, NULL); +} + +TEST("thread run") { + thrd_t t; + int data = 22; + int result; + REQUIRE(thrd_create(&t, test_run, &data) == thrd_success); + REQUIRE(thrd_join(t, &result) == thrd_success); + REQUIRE(result == 42); +} + +TEST("thread equal") { + thrd_t foo, bar; + REQUIRE(thrd_create(&foo, test_nothing, NULL) == thrd_success); + REQUIRE(thrd_create(&bar, test_nothing, NULL) == thrd_success); + REQUIRE(thrd_equal(foo, foo)); + REQUIRE(!thrd_equal(foo, bar)); + REQUIRE(!thrd_equal(foo, thrd_current())); +} + +TEST("thread exit") { + thrd_t foo; + int data; + int result; + REQUIRE(thrd_create(&foo, test_exit, &data) == thrd_success); + REQUIRE(thrd_join(foo, &result) == thrd_success); + REQUIRE(data == 1); + REQUIRE(result == 3); +} + +TEST("thread yield") { + thrd_t foo; + REQUIRE(thrd_create(&foo, test_yield, NULL) == thrd_success); + REQUIRE(thrd_join(foo, NULL) == thrd_success); +} + +TEST("thread sleep") { + thrd_t foo; + REQUIRE(thrd_create(&foo, test_sleep, NULL) == thrd_success); + REQUIRE(thrd_join(foo, NULL) == thrd_success); +} + +TEST("thread detach") { + thrd_t foo; + REQUIRE(thrd_create(&foo, test_nothing, NULL) == thrd_success); + REQUIRE(thrd_detach(foo) == thrd_success); + + struct timespec t = { .tv_sec = 0, .tv_nsec = 10000000 }; + thrd_sleep(&t, NULL); +} |