From 335f53fb645efa8face84c2f053a1ec2ed53cf47 Mon Sep 17 00:00:00 2001 From: Christian Buttner Date: Tue, 29 Apr 2025 22:50:01 +0200 Subject: [PATCH] Rework Win32 mutex, condition variable and once flag (#2111) * Rework Win32 mutex, condition variable and once flag. --- lib/std/os/win32/general.c3 | 3 +- lib/std/os/win32/process.c3 | 15 + lib/std/os/win32/types.c3 | 4 +- lib/std/threads/os/thread_none.c3 | 1 + lib/std/threads/os/thread_posix.c3 | 4 +- lib/std/threads/os/thread_win32.c3 | 440 ++++++++++++++++------------- lib/std/threads/thread.c3 | 33 ++- releasenotes.md | 2 + 8 files changed, 293 insertions(+), 209 deletions(-) diff --git a/lib/std/os/win32/general.c3 b/lib/std/os/win32/general.c3 index 9fd6b193c..355910703 100644 --- a/lib/std/os/win32/general.c3 +++ b/lib/std/os/win32/general.c3 @@ -222,4 +222,5 @@ const Win32_DWORD ERROR_MR_MID_NOT_FOUND = 0x13D; const Win32_DWORD ERROR_SCOPE_NOT_FOUND = 0x13E; const Win32_DWORD ERROR_UNDEFINED_SCOPE = 0x13F; const Win32_DWORD ERROR_IO_INCOMPLETE = 0x3E4; -const Win32_DWORD ERROR_IO_PENDING = 0x3E5; \ No newline at end of file +const Win32_DWORD ERROR_IO_PENDING = 0x3E5; +const Win32_DWORD ERROR_TIMEOUT = 0x5B4; \ No newline at end of file diff --git a/lib/std/os/win32/process.c3 b/lib/std/os/win32/process.c3 index 77831e10f..e2daf3e55 100644 --- a/lib/std/os/win32/process.c3 +++ b/lib/std/os/win32/process.c3 @@ -46,6 +46,8 @@ const IMAGE_FILE_MACHINE_ARM64 = 0xAA64; const IMAGE_FILE_MACHINE_AMD64 = 0x8664; const UNDNAME_COMPLETE = 0x0000; +alias Win32_INIT_ONCE_FN = fn Win32_BOOL(Win32_INIT_ONCE* initOnce, void* parameter, void** context); + extern fn void initializeCriticalSection(Win32_CRITICAL_SECTION* section) @extern("InitializeCriticalSection"); extern fn void deleteCriticalSection(Win32_CRITICAL_SECTION* section) @extern("DeleteCriticalSection"); extern fn Win32_HANDLE createMutex(void*, Win32_BOOL, void*) @extern("CreateMutexA"); @@ -53,6 +55,19 @@ extern fn Win32_BOOL releaseMutex(Win32_HANDLE) @extern("ReleaseMutex"); extern fn void enterCriticalSection(Win32_CRITICAL_SECTION* section) @extern("EnterCriticalSection"); extern fn void leaveCriticalSection(Win32_CRITICAL_SECTION* section) @extern("LeaveCriticalSection"); extern fn Win32_BOOL tryEnterCriticalSection(Win32_CRITICAL_SECTION* section) @extern("TryEnterCriticalSection"); +extern fn void initializeSRWLock(Win32_SRWLOCK* lock) @extern("InitializeSRWLock"); +extern fn void acquireSRWLockExclusive(Win32_SRWLOCK* lock) @extern("AcquireSRWLockExclusive"); +extern fn void acquireSRWLockShared(Win32_SRWLOCK* lock) @extern("AcquireSRWLockShared"); +extern fn void releaseSRWLockExclusive(Win32_SRWLOCK* lock) @extern("ReleaseSRWLockExclusive"); +extern fn void releaseSRWLockShared(Win32_SRWLOCK* lock) @extern("ReleaseSRWLockShared"); +extern fn Win32_BOOL tryAcquireSRWLockExclusive(Win32_SRWLOCK* lock) @extern("TryAcquireSRWLockExclusive"); +extern fn Win32_BOOL tryAcquireSRWLockShared(Win32_SRWLOCK* lock) @extern("TryAcquireSRWLockShared"); +extern fn void initializeConditionVariable(Win32_CONDITION_VARIABLE* conditionVariable) @extern("InitializeConditionVariable"); +extern fn void wakeConditionVariable(Win32_CONDITION_VARIABLE* conditionVariable) @extern("WakeConditionVariable"); +extern fn void wakeAllConditionVariable(Win32_CONDITION_VARIABLE* conditionVariable) @extern("WakeAllConditionVariable"); +extern fn Win32_BOOL sleepConditionVariableCS(Win32_CONDITION_VARIABLE* conditionVariable, Win32_CRITICAL_SECTION* section, Win32_DWORD dwMilliseconds) @extern("SleepConditionVariableCS"); +extern fn Win32_BOOL sleepConditionVariableSRW(Win32_CONDITION_VARIABLE* conditionVariable, Win32_SRWLOCK* lock, Win32_DWORD dwMilliseconds, Win32_ULONG flags) @extern("SleepConditionVariableSRW"); +extern fn Win32_BOOL initOnceExecuteOnce(Win32_INIT_ONCE* initOnce, Win32_INIT_ONCE_FN initFn, void* parameter, void** context) @extern("InitOnceExecuteOnce"); extern fn Win32_DWORD waitForSingleObject(Win32_HANDLE hHandle, Win32_DWORD dwMilliseconds) @extern("WaitForSingleObject"); extern fn Win32_DWORD waitForSingleObjectEx(Win32_HANDLE hHandle, Win32_DWORD dwMilliseconds, Win32_BOOL bAlertable) @extern("WaitForSingleObjectEx"); extern fn Win32_DWORD waitForMultipleObjects(Win32_DWORD nCount, Win32_HANDLE* lpHandles, Win32_BOOL bWaitAll, Win32_DWORD dwMilliseconds) @extern("WaitForMultipleObjects"); diff --git a/lib/std/os/win32/types.c3 b/lib/std/os/win32/types.c3 index 28aa6e352..3fae2881d 100644 --- a/lib/std/os/win32/types.c3 +++ b/lib/std/os/win32/types.c3 @@ -190,6 +190,9 @@ union Win32_LARGE_INTEGER } typedef Win32_CRITICAL_SECTION = ulong[5]; +typedef Win32_CONDITION_VARIABLE = void*; +typedef Win32_SRWLOCK = void*; +typedef Win32_INIT_ONCE = void*; struct Win32_SECURITY_ATTRIBUTES { @@ -694,4 +697,3 @@ alias Win32_LPSTACKFRAME64 = Win32_STACKFRAME64*; alias Win32_PMODLOAD_DATA = Win32_MODLOAD_DATA*; alias Win32_PIMAGEHLP_LINE64 = Win32_IMAGEHLP_LINE64*; alias Win32_LPMODULEINFO = Win32_MODULEINFO*; - diff --git a/lib/std/threads/os/thread_none.c3 b/lib/std/threads/os/thread_none.c3 index 5ff058a7d..3509433a2 100644 --- a/lib/std/threads/os/thread_none.c3 +++ b/lib/std/threads/os/thread_none.c3 @@ -1,6 +1,7 @@ module std::thread::os @if (!env::POSIX && !env::WIN32); typedef NativeMutex = int; +typedef NativeTimedMutex = int; typedef NativeConditionVariable = int; typedef NativeOnceFlag = int; typedef NativeThread = int; \ No newline at end of file diff --git a/lib/std/threads/os/thread_posix.c3 b/lib/std/threads/os/thread_posix.c3 index 59cb8387b..752914c3f 100644 --- a/lib/std/threads/os/thread_posix.c3 +++ b/lib/std/threads/os/thread_posix.c3 @@ -7,6 +7,8 @@ struct NativeMutex bool initialized; } +alias NativeTimedMutex = NativeMutex; + alias NativeConditionVariable = Pthread_cond_t; struct NativeThread @@ -28,7 +30,7 @@ fn void? NativeMutex.init(&self, MutexType type) if (posix::pthread_mutexattr_init(&attr)) return thread::INIT_FAILED?; defer posix::pthread_mutexattr_destroy(&attr); // TODO: make a fine grained error instead - if (type & thread::MUTEX_RECURSIVE) + if (type.recursive) { if (posix::pthread_mutexattr_settype(&attr, posix::PTHREAD_MUTEX_RECURSIVE)) return thread::INIT_FAILED?; } diff --git a/lib/std/threads/os/thread_win32.c3 b/lib/std/threads/os/thread_win32.c3 index a2f9fbd52..e683a8073 100644 --- a/lib/std/threads/os/thread_win32.c3 +++ b/lib/std/threads/os/thread_win32.c3 @@ -5,239 +5,313 @@ typedef NativeThread = inline Win32_HANDLE; struct NativeMutex { - union + Win32_SRWLOCK srw_lock; + Win32_DWORD owner_thread; + bitstruct : uint { - Win32_CRITICAL_SECTION critical_section; - Win32_HANDLE handle; + bool initialized : 0; + bool recursive : 1; + uint locks : 2..31; } - // Size is less than a Win32_HANDLE so due to alignment - // there is no benefit to pack these into a bitstruct. - uint locks; - bool recursive; - bool timed; } -struct NativeOnceFlag +struct NativeTimedMutex { - int status; - Win32_CRITICAL_SECTION lock; + Win32_SRWLOCK srw_lock; + Win32_CONDITION_VARIABLE cond_var; + Win32_DWORD owner_thread; + bitstruct : uint + { + bool initialized : 0; + bool recursive : 1; + uint locks : 2..31; + } } 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; + Win32_CONDITION_VARIABLE cond_var; } -fn void? NativeMutex.init(&mtx, MutexType type) +struct NativeOnceFlag { - mtx.locks = 0; - mtx.recursive = (bool)(type & thread::MUTEX_RECURSIVE); - mtx.timed = (bool)(type & thread::MUTEX_TIMED); - if (!mtx.timed) - { - win32::initializeCriticalSection(&(mtx.critical_section)); - return; - } - if (!(mtx.handle = win32::createMutex(null, 0, null))) return thread::INIT_FAILED?; + Win32_INIT_ONCE init_once; } -fn void? NativeMutex.destroy(&mtx) -{ - mtx.locks = 0; - if (!mtx.timed) - { - win32::deleteCriticalSection(&mtx.critical_section); - return; - } - if (!win32::closeHandle(mtx.handle)) return thread::DESTROY_FAILED?; -} - -fn void? NativeMutex.lock(&mtx) -{ - if (!mtx.timed) - { - win32::enterCriticalSection(&mtx.critical_section); - } - else - { - switch (win32::waitForSingleObject(mtx.handle, win32::INFINITE)) - { - case win32::WAIT_OBJECT_0: - break; - case win32::WAIT_ABANDONED: - default: - return thread::LOCK_FAILED?; - - } - } - if (!mtx.recursive && mtx.locks) - { - return thread::LOCK_FAILED?; - } - mtx.locks++; -} - - <* - @require mtx.timed : "Only available for timed locks" + @require !mtx.initialized : "Mutex is already initialized" + @require !type.timed + @ensure mtx.initialized *> -fn void? NativeMutex.lock_timeout(&mtx, ulong ms) +fn void? NativeMutex.init(&mtx, MutexType type) { - if (ms > uint.max) ms = uint.max; - switch (win32::waitForSingleObject(mtx.handle, (uint)ms)) - { - case win32::WAIT_OBJECT_0: - break; - case win32::WAIT_TIMEOUT: - return thread::LOCK_TIMEOUT?; - case win32::WAIT_ABANDONED: - default: - return thread::LOCK_FAILED?; - } - if (!mtx.recursive && mtx.locks) - { - return thread::LOCK_FAILED?; - } - mtx.locks++; + *mtx = { + .initialized = true, + .recursive = type.recursive, + }; } +<* + @require mtx.initialized : "Mutex was not initialized" + @require mtx.owner_thread != win32::getCurrentThreadId() : "Mutex was not unlocked before destroying it" + @ensure !mtx.initialized +*> +fn void? NativeMutex.destroy(&mtx) +{ + *mtx = {}; +} + +<* + @require mtx.initialized : "Mutex was not initialized" +*> +fn void? NativeMutex.lock(&mtx) +{ + Win32_DWORD current_thread = win32::getCurrentThreadId(); + if (mtx.owner_thread == current_thread) + { + if (!mtx.recursive) return thread::LOCK_FAILED?; + mtx.locks++; + return; + } + + win32::acquireSRWLockExclusive(&mtx.srw_lock); + mtx.owner_thread = current_thread; + mtx.locks = 1; +} + +<* + @require mtx.initialized : "Mutex was not initialized" +*> fn bool NativeMutex.try_lock(&mtx) { - bool success = mtx.timed - ? win32::waitForSingleObject(mtx.handle, 0) == win32::WAIT_OBJECT_0 - : (bool)win32::tryEnterCriticalSection(&mtx.critical_section); - - if (!success) return false; - if (!mtx.recursive) + Win32_DWORD current_thread = win32::getCurrentThreadId(); + if (mtx.owner_thread == current_thread) { - if (mtx.locks) - { - if (mtx.timed) - { - win32::releaseMutex(mtx.handle); - } - else - { - win32::leaveCriticalSection(&mtx.critical_section); - } - return false; - } + if (!mtx.recursive) return false; + mtx.locks++; + return true; } - mtx.locks++; + + if (!win32::tryAcquireSRWLockExclusive(&mtx.srw_lock)) return false; + + mtx.owner_thread = current_thread; + mtx.locks = 1; return true; } +<* + @require mtx.initialized : "Mutex was not initialized" +*> fn void? NativeMutex.unlock(&mtx) { - if (!mtx.locks) return thread::UNLOCK_FAILED?; - mtx.locks--; - if (!mtx.timed) + $if env::COMPILER_SAFE_MODE: + if (mtx.owner_thread != win32::getCurrentThreadId()) return thread::UNLOCK_FAILED?; // Mutex was not locked by the current thread + $endif + if (--mtx.locks == 0) { - win32::leaveCriticalSection(&mtx.critical_section); - return; + mtx.owner_thread = 0; + win32::releaseSRWLockExclusive(&mtx.srw_lock); } - if (!win32::releaseMutex(mtx.handle)) return thread::UNLOCK_FAILED?; } -const int CONDITION_EVENT_ONE = 0; -const int CONDITION_EVENT_ALL = 1; +<* + @require type.timed + @require !mtx.initialized : "Mutex is already initialized" + @ensure mtx.initialized +*> +fn void? NativeTimedMutex.init(&mtx, MutexType type) +{ + *mtx = { + .initialized = true, + .recursive = type.recursive, + }; +} + +<* + @require mtx.initialized : "Mutex was not initialized" + @require mtx.owner_thread != win32::getCurrentThreadId() : "Mutex was not unlocked before destroying it" + @ensure !mtx.initialized +*> +fn void? NativeTimedMutex.destroy(&mtx) +{ + *mtx = {}; +} + +fn void? NativeTimedMutex.wait_cond_var(&mtx, uint ms) @local +{ + if (!win32::sleepConditionVariableSRW(&mtx.cond_var, &mtx.srw_lock, ms, 0)) + { + if (win32::getLastError() != win32::ERROR_TIMEOUT) + { + return thread::WAIT_FAILED?; + } + } +} + +<* + @require mtx.initialized : "Mutex was not initialized" +*> +fn void? NativeTimedMutex.lock(&mtx) +{ + Win32_DWORD current_thread = win32::getCurrentThreadId(); + if (mtx.owner_thread == current_thread) + { + if (!mtx.recursive) return thread::LOCK_FAILED?; + mtx.locks++; + return; + } + + win32::acquireSRWLockExclusive(&mtx.srw_lock); + defer win32::releaseSRWLockExclusive(&mtx.srw_lock); + + while (mtx.locks) + { + mtx.wait_cond_var(win32::INFINITE)!; + } + mtx.locks = 1; + mtx.owner_thread = current_thread; +} + +<* + @require mtx.initialized : "Mutex was not initialized" +*> +fn void? NativeTimedMutex.lock_timeout(&mtx, ulong ms) +{ + Win32_DWORD current_thread = win32::getCurrentThreadId(); + if (mtx.owner_thread == current_thread) + { + if (!mtx.recursive) return thread::LOCK_FAILED?; + mtx.locks++; + return; + } + + win32::acquireSRWLockExclusive(&mtx.srw_lock); + defer win32::releaseSRWLockExclusive(&mtx.srw_lock); + + if (!mtx.locks) + { + // Got the lock without needing to wait + mtx.locks = 1; + mtx.owner_thread = current_thread; + return; + } + + NanoDuration duration = time::ms(ms).to_nano(); + Clock start = clock::now(); + for (NanoDuration remaining = duration; remaining > 0; remaining = duration - start.to_now()) + { + ulong remaining_ms = remaining.to_ms(); + if (remaining_ms > uint.max) remaining_ms = uint.max; + mtx.wait_cond_var((uint)remaining_ms)!; + if (!mtx.locks) + { + // Got the lock + mtx.locks = 1; + mtx.owner_thread = current_thread; + return; + } + } + return thread::WAIT_FAILED?; +} + +<* + @require mtx.initialized : "Mutex was not initialized" +*> +fn bool NativeTimedMutex.try_lock(&mtx) +{ + Win32_DWORD current_thread = win32::getCurrentThreadId(); + if (mtx.owner_thread == current_thread) + { + if (!mtx.recursive) return false; + mtx.locks++; + return true; + } + + win32::acquireSRWLockExclusive(&mtx.srw_lock); + defer win32::releaseSRWLockExclusive(&mtx.srw_lock); + + if (mtx.locks) return false; + mtx.locks = 1; + mtx.owner_thread = current_thread; + return true; +} + +<* + @require mtx.initialized : "Mutex was not initialized" +*> +fn void? NativeTimedMutex.unlock(&mtx) +{ + win32::acquireSRWLockExclusive(&mtx.srw_lock); + $if env::COMPILER_SAFE_MODE: + if (mtx.owner_thread != win32::getCurrentThreadId()) + { + win32::releaseSRWLockExclusive(&mtx.srw_lock); + return thread::UNLOCK_FAILED?; // Mutex was not locked by the current thread + } + $endif + + bool signal; + if (--mtx.locks == 0) + { + mtx.owner_thread = 0; + signal = true; + } + win32::releaseSRWLockExclusive(&mtx.srw_lock); + if (signal) win32::wakeConditionVariable(&mtx.cond_var); +} fn void? NativeConditionVariable.init(&cond) { - cond.waiters_count = 0; - win32::initializeCriticalSection(&cond.waiters_count_lock); - cond.event_one = win32::createEventA(null, 0, 0, null); - if (!cond.event_one) - { - cond.event_all = (Win32_HANDLE)0; - return thread::INIT_FAILED?; - } - cond.event_all = win32::createEventA(null, 1, 0, null); - if (!cond.event_all) - { - win32::closeHandle(cond.event_one); - cond.event_one = (Win32_HANDLE)0; - return thread::INIT_FAILED?; - } + cond.cond_var = {}; } fn void? NativeConditionVariable.destroy(&cond) @maydiscard { - if (cond.event_one) win32::closeHandle(cond.event_one); - if (cond.event_all) win32::closeHandle(cond.event_all); - win32::deleteCriticalSection(&cond.waiters_count_lock); + // Nothing to do } fn void? NativeConditionVariable.signal(&cond) { - win32::enterCriticalSection(&cond.waiters_count_lock); - bool have_waiters = cond.waiters_count > 0; - win32::leaveCriticalSection(&cond.waiters_count_lock); - if (have_waiters && !win32::setEvent(cond.event_one)) return thread::SIGNAL_FAILED?; + win32::wakeConditionVariable(&cond.cond_var); } fn void? NativeConditionVariable.broadcast(&cond) { - win32::enterCriticalSection(&cond.waiters_count_lock); - bool have_waiters = cond.waiters_count > 0; - win32::leaveCriticalSection(&cond.waiters_count_lock); - if (have_waiters && !win32::setEvent(cond.event_all)) return thread::SIGNAL_FAILED?; + win32::wakeAllConditionVariable(&cond.cond_var); } fn void? timedwait(NativeConditionVariable* cond, NativeMutex* mtx, uint timeout) @private { - win32::enterCriticalSection(&cond.waiters_count_lock); - cond.waiters_count++; - win32::leaveCriticalSection(&cond.waiters_count_lock); - - mtx.unlock()!; - - uint result = win32::waitForMultipleObjects(2, &cond.events, 0, timeout); - switch (result) + Win32_DWORD owner_thread = mtx.owner_thread; + if (mtx.locks != 1 || owner_thread != win32::getCurrentThreadId()) return thread::WAIT_FAILED?; + mtx.owner_thread = 0; + mtx.locks = 0; + defer { - case win32::WAIT_TIMEOUT: - mtx.lock()!; - return thread::WAIT_TIMEOUT?; - case win32::WAIT_FAILED: - mtx.lock()!; - return thread::WAIT_FAILED?; - default: - break; + mtx.owner_thread = owner_thread; + mtx.locks = 1; } - - win32::enterCriticalSection(&cond.waiters_count_lock); - cond.waiters_count--; - // If event all && no waiters - bool last_waiter = result == 1 && !cond.waiters_count; - win32::leaveCriticalSection(&cond.waiters_count_lock); - - if (last_waiter) + if (!win32::sleepConditionVariableSRW(&cond.cond_var, &mtx.srw_lock, timeout, 0)) { - if (!win32::resetEvent(cond.event_all)) + if (win32::getLastError() == win32::ERROR_TIMEOUT) { - mtx.lock()!; - return thread::WAIT_FAILED?; + return thread::WAIT_TIMEOUT?; } + return thread::WAIT_FAILED?; } - - mtx.lock()!; } +<* + @require mtx.initialized : "Mutex was not initialized" +*> fn void? NativeConditionVariable.wait(&cond, NativeMutex* mtx) @inline { return timedwait(cond, mtx, win32::INFINITE) @inline; } +<* + @require mtx.initialized : "Mutex was not initialized" +*> fn void? NativeConditionVariable.wait_timeout(&cond, NativeMutex* mtx, ulong ms) @inline { if (ms > uint.max) ms = uint.max; @@ -267,30 +341,12 @@ fn void native_thread_yield() fn void NativeOnceFlag.call_once(&flag, OnceFn func) { - while (@volatile_load(flag.status) < 3) + var callback = fn Win32_BOOL(Win32_INIT_ONCE* init_once, void* parameter, void** context) { - switch (@volatile_load(flag.status)) - { - case 0: - if (mem::compare_exchange_volatile(&flag.status, 1, 0, AtomicOrdering.SEQ_CONSISTENT, AtomicOrdering.SEQ_CONSISTENT) == 0) - { - win32::initializeCriticalSection(&flag.lock); - win32::enterCriticalSection(&flag.lock); - @volatile_store(flag.status, 2); - func(); - @volatile_store(flag.status, 3); - win32::leaveCriticalSection(&flag.lock); - return; - } - break; - case 1: - break; - case 2: - win32::enterCriticalSection(&flag.lock); - win32::leaveCriticalSection(&flag.lock); - break; - } - } + ((OnceFn)parameter)(); + return 1; + }; + win32::initOnceExecuteOnce(&flag.init_once, callback, func, null); } fn int? NativeThread.join(thread) diff --git a/lib/std/threads/thread.c3 b/lib/std/threads/thread.c3 index 47e673de7..26614bc1d 100644 --- a/lib/std/threads/thread.c3 +++ b/lib/std/threads/thread.c3 @@ -2,16 +2,16 @@ module std::thread; import std::thread::os; import std::time; -typedef MutexType = int; - -const MutexType MUTEX_PLAIN = 0; -const MutexType MUTEX_TIMED = 1; -const MutexType MUTEX_RECURSIVE = 2; +bitstruct MutexType : int +{ + bool timed; + bool recursive; +} typedef Mutex = NativeMutex; -typedef TimedMutex = inline Mutex; typedef RecursiveMutex = inline Mutex; -typedef TimedRecursiveMutex = inline Mutex; +typedef TimedMutex = NativeTimedMutex; +typedef TimedRecursiveMutex = inline TimedMutex; typedef ConditionVariable = NativeConditionVariable; typedef Thread = inline NativeThread; typedef OnceFlag = NativeOnceFlag; @@ -33,16 +33,21 @@ faultdef INTERRUPTED, CHANNEL_CLOSED; -macro void? Mutex.init(&mutex) => NativeMutex.init((NativeMutex*)mutex, MUTEX_PLAIN); -macro void? TimedMutex.init(&mutex) => NativeMutex.init((NativeMutex*)mutex, MUTEX_TIMED); -macro void? RecursiveMutex.init(&mutex) => NativeMutex.init((NativeMutex*)mutex, MUTEX_RECURSIVE); -macro void? TimedRecursiveMutex.init(&mutex) => NativeMutex.init((NativeMutex*)mutex, MUTEX_TIMED | MUTEX_RECURSIVE); +macro void? Mutex.init(&mutex) => NativeMutex.init((NativeMutex*)mutex, {}); +macro void? RecursiveMutex.init(&mutex) => NativeMutex.init((NativeMutex*)mutex, {.recursive}); macro void? Mutex.destroy(&mutex) => NativeMutex.destroy((NativeMutex*)mutex); macro void? Mutex.lock(&mutex) => NativeMutex.lock((NativeMutex*)mutex); -macro void? TimedMutex.lock_timeout(&mutex, ulong ms) => NativeMutex.lock_timeout((NativeMutex*)mutex, ms); -macro void? TimedRecursiveMutex.lock_timeout(&mutex, ulong ms) => NativeMutex.lock_timeout((NativeMutex*)mutex, ms); -macro bool Mutex.try_lock(&mutex) => NativeMutex.try_lock((NativeMutex*)mutex); +macro bool Mutex.try_lock(&mutex) => NativeMutex.try_lock((NativeMutex*)mutex); macro void? Mutex.unlock(&mutex) => NativeMutex.unlock((NativeMutex*)mutex); + +macro void? TimedMutex.init(&mutex) => NativeTimedMutex.init((NativeTimedMutex*)mutex, {.timed}); +macro void? TimedRecursiveMutex.init(&mutex) => NativeTimedMutex.init((NativeTimedMutex*)mutex, {.timed, .recursive}); +macro void? TimedMutex.destroy(&mutex) => NativeTimedMutex.destroy((NativeTimedMutex*)mutex); +macro void? TimedMutex.lock(&mutex) => NativeTimedMutex.lock((NativeTimedMutex*)mutex); +macro void? TimedMutex.lock_timeout(&mutex, ulong ms) => NativeTimedMutex.lock_timeout((NativeTimedMutex*)mutex, ms); +macro bool TimedMutex.try_lock(&mutex) => NativeTimedMutex.try_lock((NativeTimedMutex*)mutex); +macro void? TimedMutex.unlock(&mutex) => NativeTimedMutex.unlock((NativeTimedMutex*)mutex); + macro void Mutex.@in_lock(&mutex; @body) { (void)mutex.lock(); diff --git a/releasenotes.md b/releasenotes.md index aa23e029f..08d04c2d6 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -19,6 +19,7 @@ - Deprecated old inference with slice copy. Copying must now ensure a slicing operator at the end of the right hand side: `foo[1..2] = bar[..]` rather than the old `foo[1..2] = bar`. The old behaviour can be mostly retained with `--use-old-slice-copy`). - Added `Enum.lookup` and `Enum.lookup_field`. - `c3c build` picks first target rather than the first executable #2105. +- New Win32 Mutex, ConditionVariable and OnceFlag implementation ### Fixes - Trying to cast an enum to int and back caused the compiler to crash. @@ -38,6 +39,7 @@ - `@ensure` should be allowed to read "out" variables. #2107 - Error message for casting generic to incompatible type does not work properly with nested generics #1953 - Fixed enum regression after 0.7.0 enum change. +- ConditionVariable now properly works on Win32 ### Stdlib changes - Hash functions for integer vectors and arrays.