module std::thread::os @if(env::WIN32); import std::os::win32, std::time; typedef NativeThread = inline Win32_HANDLE; struct NativeMutex { Win32_SRWLOCK srw_lock; Win32_DWORD owner_thread; bitstruct : uint { bool initialized : 0; bool recursive : 1; uint locks : 2..31; } } struct NativeTimedMutex { 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 { Win32_CONDITION_VARIABLE cond_var; } struct NativeOnceFlag { Win32_INIT_ONCE init_once; } <* @require !mtx.initialized : "Mutex is already initialized" @require !type.timed @ensure mtx.initialized *> fn void? NativeMutex.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? 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) { Win32_DWORD current_thread = win32::getCurrentThreadId(); if (mtx.owner_thread == current_thread) { if (!mtx.recursive) return false; mtx.locks++; return true; } 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 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) { mtx.owner_thread = 0; win32::releaseSRWLockExclusive(&mtx.srw_lock); } } <* @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.cond_var = {}; } fn void? NativeConditionVariable.destroy(&cond) @maydiscard { // Nothing to do } fn void? NativeConditionVariable.signal(&cond) { win32::wakeConditionVariable(&cond.cond_var); } fn void? NativeConditionVariable.broadcast(&cond) { win32::wakeAllConditionVariable(&cond.cond_var); } fn void? timedwait(NativeConditionVariable* cond, NativeMutex* mtx, uint timeout) @private { 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 { mtx.owner_thread = owner_thread; mtx.locks = 1; } if (!win32::sleepConditionVariableSRW(&cond.cond_var, &mtx.srw_lock, timeout, 0)) { if (win32::getLastError() == win32::ERROR_TIMEOUT) { return thread::WAIT_TIMEOUT?; } return thread::WAIT_FAILED?; } } <* @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; return timedwait(cond, mtx, (uint)ms) @inline; } fn void? NativeThread.create(&thread, ThreadFn func, void* args) { if (!(*thread = (NativeThread)win32::createThread(null, 0, func, args, 0, null))) return thread::INIT_FAILED?; } fn void? NativeThread.detach(thread) @inline { if (!win32::closeHandle(thread)) return thread::DETACH_FAILED?; } fn void native_thread_exit(int result) @inline { win32::exitThread((uint)result); } fn void native_thread_yield() { win32::sleep(0); } fn void NativeOnceFlag.call_once(&flag, OnceFn func) { var callback = fn Win32_BOOL(Win32_INIT_ONCE* init_once, void* parameter, void** context) { ((OnceFn)parameter)(); return 1; }; win32::initOnceExecuteOnce(&flag.init_once, callback, func, null); } fn int? NativeThread.join(thread) { uint res; if (win32::waitForSingleObject(thread, win32::INFINITE) == win32::WAIT_FAILED) return thread::JOIN_FAILED?; if (!win32::getExitCodeThread(thread, &res)) return thread::JOIN_FAILED?; defer win32::closeHandle(thread); return res; } fn NativeThread native_thread_current() { return (NativeThread)win32::getCurrentThread(); } fn bool NativeThread.equals(thread, NativeThread other) { return win32::getThreadId(thread) == win32::getThreadId(other); } fn void? native_sleep_nano(NanoDuration ns) { long ms = ns.to_ms(); if (ms <= 0) return; if (ms > Win32_DWORD.max) ms = Win32_DWORD.max; if (win32::sleepEx((Win32_DWORD)ms, (Win32_BOOL)true) == win32::WAIT_IO_COMPLETION) return thread::INTERRUPTED?; }