From b6f302d1c63ca9823c1a49ea1071edbfdc21baaa Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Sat, 12 Aug 2023 01:44:39 +0200 Subject: [PATCH] Fix issues with thread, add some simple test. --- lib/std/os/win32/process.c3 | 34 --------------- lib/std/threads/os/thread_none.c3 | 2 +- lib/std/threads/os/thread_posix.c3 | 50 +++++++++++------------ lib/std/threads/os/thread_win32.c3 | 45 ++++++++++++++++++-- lib/std/threads/thread.c3 | 14 +------ test/unit/stdlib/threads/simple_thread.c3 | 42 +++++++++++++++++++ 6 files changed, 111 insertions(+), 76 deletions(-) create mode 100644 test/unit/stdlib/threads/simple_thread.c3 diff --git a/lib/std/os/win32/process.c3 b/lib/std/os/win32/process.c3 index 24cc2e7b6..6d703b744 100644 --- a/lib/std/os/win32/process.c3 +++ b/lib/std/os/win32/process.c3 @@ -29,40 +29,6 @@ const Win32_DWORD PIPE_NOWAIT = 1; const Win32_DWORD PIPE_ACCEPT_REMOTE_CLIENTS = 0; const Win32_DWORD PIPE_REJECT_REMOTE_CLIENTS = 8; -def NativeThread = Win32_HANDLE; - -struct NativeMutex -{ - union - { - Win32_CRITICAL_SECTION critical_section; - Win32_HANDLE handle; - } - bool already_locked; - bool recursive; - bool timed; -} - -struct NativeOnceFlag -{ - int status; - Win32_CRITICAL_SECTION lock; -} - -struct NativeConditionVariable -{ - union - { - struct - { - Win32_HANDLE event_one; - Win32_HANDLE event_all; - } - Win32_HANDLE[2] events; - } - uint waiters_count; - Win32_CRITICAL_SECTION waiters_count_lock; -} extern fn void initializeCriticalSection(Win32_CRITICAL_SECTION* section) @extern("InitializeCriticalSection"); extern fn void deleteCriticalSection(Win32_CRITICAL_SECTION* section) @extern("DeleteCriticalSection"); diff --git a/lib/std/threads/os/thread_none.c3 b/lib/std/threads/os/thread_none.c3 index 633c55e18..17f0f623c 100644 --- a/lib/std/threads/os/thread_none.c3 +++ b/lib/std/threads/os/thread_none.c3 @@ -1,4 +1,4 @@ -module std::thread; +module std::thread::os @if (!env::POSIX && !env::WIN32); def NativeMutex = distinct int; def NativeConditionVariable = distinct int; diff --git a/lib/std/threads/os/thread_posix.c3 b/lib/std/threads/os/thread_posix.c3 index be1249144..3647287af 100644 --- a/lib/std/threads/os/thread_posix.c3 +++ b/lib/std/threads/os/thread_posix.c3 @@ -1,6 +1,6 @@ -module std::os::posix @if(env::POSIX); +module std::threads::os @if(env::POSIX); +import std::os::posix; import libc; - def NativeMutex = Pthread_mutex_t; def NativeConditionVariable = Pthread_cond_t; def NativeThread = Pthread_t; @@ -9,30 +9,30 @@ def NativeOnceFlag = Pthread_once_t; fn void! NativeMutex.init(&mtx, MutexType type) { Pthread_mutexattr_t attr; - if (pthread_mutexattr_init(&attr)) return ThreadFault.INIT_FAILED?; - defer pthread_mutexattr_destroy(&attr); + if (posix::pthread_mutexattr_init(&attr)) return ThreadFault.INIT_FAILED?; + defer posix::pthread_mutexattr_destroy(&attr); if (type & thread::MUTEX_RECURSIVE) { - if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)) return ThreadFault.INIT_FAILED?; + if (posix::pthread_mutexattr_settype(&attr, posix::PTHREAD_MUTEX_RECURSIVE)) return ThreadFault.INIT_FAILED?; } - if (pthread_mutex_init(mtx, &attr)) return ThreadFault.INIT_FAILED?; + if (posix::pthread_mutex_init(mtx, &attr)) return ThreadFault.INIT_FAILED?; } fn void! NativeMutex.destroy(&mtx) { - if (pthread_mutex_destroy(mtx)) return ThreadFault.DESTROY_FAILED?; + if (posix::pthread_mutex_destroy(mtx)) return ThreadFault.DESTROY_FAILED?; } fn void! NativeMutex.lock(&mtx) { - if (pthread_mutex_lock(mtx)) return ThreadFault.LOCK_FAILED?; + if (posix::pthread_mutex_lock(mtx)) return ThreadFault.LOCK_FAILED?; } fn void! NativeMutex.lock_timeoutout(&mtx, ulong ms) { /* Try to acquire the lock and, if we fail, sleep for 5ms. */ Errno result; - while ((result = pthread_mutex_trylock(mtx)) == errno::EBUSY) + while ((result = posix::pthread_mutex_trylock(mtx)) == errno::EBUSY) { if (!ms) break; ulong sleep = min(5, ms); @@ -53,37 +53,37 @@ fn void! NativeMutex.lock_timeoutout(&mtx, ulong ms) fn bool NativeMutex.try_lock(&mtx) { - return !pthread_mutex_trylock(mtx); + return !posix::pthread_mutex_trylock(mtx); } fn void! NativeMutex.unlock(&mtx) { - if (pthread_mutex_unlock(mtx)) return ThreadFault.UNLOCK_FAILED?; + if (posix::pthread_mutex_unlock(mtx)) return ThreadFault.UNLOCK_FAILED?; } fn void! NativeConditionVariable.init(&cond) { - if (pthread_cond_init(cond, null)) return ThreadFault.INIT_FAILED?; + if (posix::pthread_cond_init(cond, null)) return ThreadFault.INIT_FAILED?; } fn void! NativeConditionVariable.destroy(&cond) { - if (pthread_cond_destroy(cond)) return ThreadFault.DESTROY_FAILED?; + if (posix::pthread_cond_destroy(cond)) return ThreadFault.DESTROY_FAILED?; } fn void! NativeConditionVariable.signal(&cond) { - if (pthread_cond_signal(cond)) return ThreadFault.SIGNAL_FAILED?; + if (posix::pthread_cond_signal(cond)) return ThreadFault.SIGNAL_FAILED?; } fn void! NativeConditionVariable.broadcast(&cond) { - if (pthread_cond_broadcast(cond)) return ThreadFault.SIGNAL_FAILED?; + if (posix::pthread_cond_broadcast(cond)) return ThreadFault.SIGNAL_FAILED?; } fn void! NativeConditionVariable.wait(&cond, NativeMutex* mtx) { - if (pthread_cond_wait(cond, mtx)) return ThreadFault.WAIT_FAILED?; + if (posix::pthread_cond_wait(cond, mtx)) return ThreadFault.WAIT_FAILED?; } fn void! NativeConditionVariable.wait_timeout(&cond, NativeMutex* mtx, ulong ms) @@ -92,7 +92,7 @@ fn void! NativeConditionVariable.wait_timeout(&cond, NativeMutex* mtx, ulong ms) if (libc::timespec_get(&now, libc::TIME_UTC) != libc::TIME_UTC) return ThreadFault.WAIT_FAILED?; now.s += (Time_t)(ms / 1000); now.ns += (CLong)((ms % 1000) * 1000_000); - switch (pthread_cond_timedwait(cond, mtx, &now)) + switch (posix::pthread_cond_timedwait(cond, mtx, &now)) { case errno::ETIMEDOUT: return ThreadFault.WAIT_TIMEOUT?; @@ -114,7 +114,7 @@ fn void! NativeThread.create(&thread, ThreadFn thread_fn, void* arg) { PosixThreadData *thread_data = malloc(PosixThreadData); *thread_data = { .thread_fn = thread_fn, .arg = arg }; - if (pthread_create(thread, null, &callback, thread_data) != 0) + if (posix::pthread_create(thread, null, &callback, thread_data) != 0) { *thread = null; free(thread_data); @@ -124,39 +124,39 @@ fn void! NativeThread.create(&thread, ThreadFn thread_fn, void* arg) fn void! NativeThread.detach(thread) { - if (!pthread_detach(thread)) return ThreadFault.DETACH_FAILED?; + if (!posix::pthread_detach(thread)) return ThreadFault.DETACH_FAILED?; } fn void native_thread_exit(int result) { - pthread_exit((void*)(iptr)result); + posix::pthread_exit((void*)(iptr)result); } fn NativeThread native_thread_current() { - return pthread_self(); + return (NativeThread)posix::pthread_self(); } fn bool NativeThread.equals(thread, NativeThread other) { - return (bool)pthread_equal(thread, other); + return (bool)posix::pthread_equal(thread, other); } fn int! NativeThread.join(thread) { void *pres; - if (pthread_join(thread, &pres)) return ThreadFault.JOIN_FAILED?; + if (posix::pthread_join(thread, &pres)) return ThreadFault.JOIN_FAILED?; return (int)(iptr)pres; } fn void NativeOnceFlag.call_once(&flag, OnceFn func) { - pthread_once(flag, func); + posix::pthread_once(flag, func); } fn void native_thread_yield() { - sched_yield(); + posix::sched_yield(); } struct PosixThreadData @private diff --git a/lib/std/threads/os/thread_win32.c3 b/lib/std/threads/os/thread_win32.c3 index 7aec431eb..5b1a8b150 100644 --- a/lib/std/threads/os/thread_win32.c3 +++ b/lib/std/threads/os/thread_win32.c3 @@ -1,6 +1,41 @@ module std::thread::os @if(env::WIN32); import std::os::win32; +def NativeThread = distinct inline Win32_HANDLE; + +struct NativeMutex +{ + union + { + Win32_CRITICAL_SECTION critical_section; + Win32_HANDLE handle; + } + bool already_locked; + bool recursive; + bool timed; +} + +struct NativeOnceFlag +{ + int status; + Win32_CRITICAL_SECTION lock; +} + +struct NativeConditionVariable +{ + union + { + struct + { + Win32_HANDLE event_one; + Win32_HANDLE event_all; + } + Win32_HANDLE[2] events; + } + uint waiters_count; + Win32_CRITICAL_SECTION waiters_count_lock; +} + fn void! NativeMutex.init(&mtx, MutexType type) { mtx.already_locked = false; @@ -199,7 +234,7 @@ fn void! NativeConditionVariable.wait_timeout(&cond, NativeMutex* mtx, uint time fn void! NativeThread.create(&thread, ThreadFn func, void* args) { - if (!(*thread = win32::createThread(null, 0, func, args, 0, null))) return ThreadFault.INIT_FAILED?; + if (!(*thread = (NativeThread)win32::createThread(null, 0, func, args, 0, null))) return ThreadFault.INIT_FAILED?; } fn void! NativeThread.detach(thread) @inline @@ -246,16 +281,18 @@ fn void NativeOnceFlag.call_once(&flag, OnceFn func) } } -fn void! NativeThread.join(thread, int *res) +fn int! NativeThread.join(thread) { + uint res; if (win32::waitForSingleObject(thread, win32::INFINITE) == win32::WAIT_FAILED) return ThreadFault.JOIN_FAILED?; - if (!win32::getExitCodeThread(thread, (uint*)res)) return ThreadFault.JOIN_FAILED?; + if (!win32::getExitCodeThread(thread, &res)) return ThreadFault.JOIN_FAILED?; defer win32::closeHandle(thread); + return res; } fn NativeThread native_thread_current() { - return win32::getCurrentThread(); + return (NativeThread)win32::getCurrentThread(); } fn bool NativeThread.equals(thread, NativeThread other) diff --git a/lib/std/threads/thread.c3 b/lib/std/threads/thread.c3 index 3214e080c..a4ac36a53 100644 --- a/lib/std/threads/thread.c3 +++ b/lib/std/threads/thread.c3 @@ -1,15 +1,5 @@ module std::thread; - -enum ThreadModel -{ - WIN32, - POSIX, - NONE -} - -const ThreadModel THREAD_MODEL = env::LIBC - ? (env::WIN32 ? ThreadModel.WIN32 : ThreadModel.POSIX) - : ThreadModel.NONE; +import std::thread::os; def MutexType = distinct int; @@ -45,7 +35,7 @@ macro void! Mutex.destroy(&mutex) => NativeMutex.destroy((NativeMutex*)mutex); macro void! Mutex.lock(&mutex) => NativeMutex.lock((NativeMutex*)mutex); macro void! Mutex.lock_timeout(&mutex, ulong ms) => NativeMutex.lock((NativeMutex*)mutex, ms); macro bool Mutex.try_lock(&mutex) => NativeMutex.try_lock((NativeMutex*)mutex); -macro bool Mutex.unlock(&mutex) => NativeMutex.unlock((NativeMutex*)mutex); +macro void! Mutex.unlock(&mutex) => NativeMutex.unlock((NativeMutex*)mutex); macro void! ConditionVariable.init(&cond) => NativeConditionVariable.init((NativeConditionVariable*)cond); macro void! ConditionVariable.destroy(&cond) => NativeConditionVariable.destroy((NativeConditionVariable*)cond); diff --git a/test/unit/stdlib/threads/simple_thread.c3 b/test/unit/stdlib/threads/simple_thread.c3 new file mode 100644 index 000000000..9249ae0ca --- /dev/null +++ b/test/unit/stdlib/threads/simple_thread.c3 @@ -0,0 +1,42 @@ +import std::thread; +import std::io; +int a; +fn void! testrun() @test +{ + Thread t; + a = 0; + t.create(fn int(void* arg) { a++; return 0; }, null); + assert(t.join()! == 0); + assert(a == 1); +} + +Mutex m; + +fn void! testrun_mutex() @test +{ + Thread[100] ts; + a = 0; + m.init(thread::MUTEX_PLAIN); + foreach (&t : ts) + { + t.create(fn int(void* arg) { + m.lock(); + defer m.unlock()!!; + a += 10; + thread::sleep_ms(5); + a *= 10; + thread::sleep_ms(5); + a /= 10; + thread::sleep_ms(5); + a -= 10; + thread::sleep_ms(5); + a++; + return 0; + }, null); + } + foreach (i, &t : ts) + { + assert(t.join()! == 0); + } + assert(a == 100); +} \ No newline at end of file