summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMitya Selivanov <0x7fffff@guattari.ru>2022-08-18 06:50:13 +0400
committerMitya Selivanov <0x7fffff@guattari.ru>2022-08-18 06:50:13 +0400
commit5f80c8b54d011580383810597aa68565ddfb8f3c (patch)
tree3bf431e61e580a68de55840fa016dbfa0767d4e9
parentac4face2d7d6d2b033874d3e6e24d2133c96132f (diff)
downloadkit-5f80c8b54d011580383810597aa68565ddfb8f3c.zip
[test] thread, mutex, condition variable
-rw-r--r--LICENSE2
-rw-r--r--README.md11
-rw-r--r--source/kit/CMakeLists.txt11
-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_win32.h21
-rw-r--r--source/kit/mersenne_twister_64.c2
-rw-r--r--source/kit/threads.h (renamed from source/kit/c11/threads.h)119
-rw-r--r--source/kit/threads.posix.c (renamed from source/kit/c11/impl/threads_posix.c)38
-rw-r--r--source/kit/threads.win32.c (renamed from source/kit/c11/impl/threads_win32.c)60
-rw-r--r--source/kit/threads.win32.h25
-rw-r--r--source/kit/threads_tls_callback.win32.cpp (renamed from source/kit/c11/impl/threads_win32_tls_callback.cpp)7
-rw-r--r--source/kit/time.c (renamed from source/kit/c11/impl/time.c)2
-rw-r--r--source/kit/time.h (renamed from source/kit/c11/time.h)6
-rw-r--r--source/test/unittests/CMakeLists.txt7
-rw-r--r--source/test/unittests/c11_threads.c6
-rw-r--r--source/test/unittests/condition_variable.test.c60
-rw-r--r--source/test/unittests/mutex.test.c74
-rw-r--r--source/test/unittests/thread.test.c78
19 files changed, 382 insertions, 160 deletions
diff --git a/LICENSE b/LICENSE
index 07dde7d..8959d98 100644
--- a/LICENSE
+++ b/LICENSE
@@ -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
diff --git a/README.md b/README.md
index 83935bf..0e59887 100644
--- a/README.md
+++ b/README.md
@@ -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) &param, 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);
+}