mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Rework Win32 mutex, condition variable and once flag (#2111)
* Rework Win32 mutex, condition variable and once flag.
This commit is contained in:
committed by
GitHub
parent
3636898ac0
commit
335f53fb64
@@ -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_SCOPE_NOT_FOUND = 0x13E;
|
||||||
const Win32_DWORD ERROR_UNDEFINED_SCOPE = 0x13F;
|
const Win32_DWORD ERROR_UNDEFINED_SCOPE = 0x13F;
|
||||||
const Win32_DWORD ERROR_IO_INCOMPLETE = 0x3E4;
|
const Win32_DWORD ERROR_IO_INCOMPLETE = 0x3E4;
|
||||||
const Win32_DWORD ERROR_IO_PENDING = 0x3E5;
|
const Win32_DWORD ERROR_IO_PENDING = 0x3E5;
|
||||||
|
const Win32_DWORD ERROR_TIMEOUT = 0x5B4;
|
||||||
@@ -46,6 +46,8 @@ const IMAGE_FILE_MACHINE_ARM64 = 0xAA64;
|
|||||||
const IMAGE_FILE_MACHINE_AMD64 = 0x8664;
|
const IMAGE_FILE_MACHINE_AMD64 = 0x8664;
|
||||||
const UNDNAME_COMPLETE = 0x0000;
|
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 initializeCriticalSection(Win32_CRITICAL_SECTION* section) @extern("InitializeCriticalSection");
|
||||||
extern fn void deleteCriticalSection(Win32_CRITICAL_SECTION* section) @extern("DeleteCriticalSection");
|
extern fn void deleteCriticalSection(Win32_CRITICAL_SECTION* section) @extern("DeleteCriticalSection");
|
||||||
extern fn Win32_HANDLE createMutex(void*, Win32_BOOL, void*) @extern("CreateMutexA");
|
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 enterCriticalSection(Win32_CRITICAL_SECTION* section) @extern("EnterCriticalSection");
|
||||||
extern fn void leaveCriticalSection(Win32_CRITICAL_SECTION* section) @extern("LeaveCriticalSection");
|
extern fn void leaveCriticalSection(Win32_CRITICAL_SECTION* section) @extern("LeaveCriticalSection");
|
||||||
extern fn Win32_BOOL tryEnterCriticalSection(Win32_CRITICAL_SECTION* section) @extern("TryEnterCriticalSection");
|
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 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 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");
|
extern fn Win32_DWORD waitForMultipleObjects(Win32_DWORD nCount, Win32_HANDLE* lpHandles, Win32_BOOL bWaitAll, Win32_DWORD dwMilliseconds) @extern("WaitForMultipleObjects");
|
||||||
|
|||||||
@@ -190,6 +190,9 @@ union Win32_LARGE_INTEGER
|
|||||||
}
|
}
|
||||||
|
|
||||||
typedef Win32_CRITICAL_SECTION = ulong[5];
|
typedef Win32_CRITICAL_SECTION = ulong[5];
|
||||||
|
typedef Win32_CONDITION_VARIABLE = void*;
|
||||||
|
typedef Win32_SRWLOCK = void*;
|
||||||
|
typedef Win32_INIT_ONCE = void*;
|
||||||
|
|
||||||
struct Win32_SECURITY_ATTRIBUTES
|
struct Win32_SECURITY_ATTRIBUTES
|
||||||
{
|
{
|
||||||
@@ -694,4 +697,3 @@ alias Win32_LPSTACKFRAME64 = Win32_STACKFRAME64*;
|
|||||||
alias Win32_PMODLOAD_DATA = Win32_MODLOAD_DATA*;
|
alias Win32_PMODLOAD_DATA = Win32_MODLOAD_DATA*;
|
||||||
alias Win32_PIMAGEHLP_LINE64 = Win32_IMAGEHLP_LINE64*;
|
alias Win32_PIMAGEHLP_LINE64 = Win32_IMAGEHLP_LINE64*;
|
||||||
alias Win32_LPMODULEINFO = Win32_MODULEINFO*;
|
alias Win32_LPMODULEINFO = Win32_MODULEINFO*;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
module std::thread::os @if (!env::POSIX && !env::WIN32);
|
module std::thread::os @if (!env::POSIX && !env::WIN32);
|
||||||
|
|
||||||
typedef NativeMutex = int;
|
typedef NativeMutex = int;
|
||||||
|
typedef NativeTimedMutex = int;
|
||||||
typedef NativeConditionVariable = int;
|
typedef NativeConditionVariable = int;
|
||||||
typedef NativeOnceFlag = int;
|
typedef NativeOnceFlag = int;
|
||||||
typedef NativeThread = int;
|
typedef NativeThread = int;
|
||||||
@@ -7,6 +7,8 @@ struct NativeMutex
|
|||||||
bool initialized;
|
bool initialized;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
alias NativeTimedMutex = NativeMutex;
|
||||||
|
|
||||||
alias NativeConditionVariable = Pthread_cond_t;
|
alias NativeConditionVariable = Pthread_cond_t;
|
||||||
|
|
||||||
struct NativeThread
|
struct NativeThread
|
||||||
@@ -28,7 +30,7 @@ fn void? NativeMutex.init(&self, MutexType type)
|
|||||||
if (posix::pthread_mutexattr_init(&attr)) return thread::INIT_FAILED?;
|
if (posix::pthread_mutexattr_init(&attr)) return thread::INIT_FAILED?;
|
||||||
defer posix::pthread_mutexattr_destroy(&attr);
|
defer posix::pthread_mutexattr_destroy(&attr);
|
||||||
// TODO: make a fine grained error instead
|
// 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?;
|
if (posix::pthread_mutexattr_settype(&attr, posix::PTHREAD_MUTEX_RECURSIVE)) return thread::INIT_FAILED?;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,239 +5,313 @@ typedef NativeThread = inline Win32_HANDLE;
|
|||||||
|
|
||||||
struct NativeMutex
|
struct NativeMutex
|
||||||
{
|
{
|
||||||
union
|
Win32_SRWLOCK srw_lock;
|
||||||
|
Win32_DWORD owner_thread;
|
||||||
|
bitstruct : uint
|
||||||
{
|
{
|
||||||
Win32_CRITICAL_SECTION critical_section;
|
bool initialized : 0;
|
||||||
Win32_HANDLE handle;
|
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_SRWLOCK srw_lock;
|
||||||
Win32_CRITICAL_SECTION lock;
|
Win32_CONDITION_VARIABLE cond_var;
|
||||||
|
Win32_DWORD owner_thread;
|
||||||
|
bitstruct : uint
|
||||||
|
{
|
||||||
|
bool initialized : 0;
|
||||||
|
bool recursive : 1;
|
||||||
|
uint locks : 2..31;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct NativeConditionVariable
|
struct NativeConditionVariable
|
||||||
{
|
{
|
||||||
union
|
Win32_CONDITION_VARIABLE cond_var;
|
||||||
{
|
|
||||||
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)
|
struct NativeOnceFlag
|
||||||
{
|
{
|
||||||
mtx.locks = 0;
|
Win32_INIT_ONCE init_once;
|
||||||
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?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
*mtx = {
|
||||||
switch (win32::waitForSingleObject(mtx.handle, (uint)ms))
|
.initialized = true,
|
||||||
{
|
.recursive = type.recursive,
|
||||||
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++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<*
|
||||||
|
@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)
|
fn bool NativeMutex.try_lock(&mtx)
|
||||||
{
|
{
|
||||||
bool success = mtx.timed
|
Win32_DWORD current_thread = win32::getCurrentThreadId();
|
||||||
? win32::waitForSingleObject(mtx.handle, 0) == win32::WAIT_OBJECT_0
|
if (mtx.owner_thread == current_thread)
|
||||||
: (bool)win32::tryEnterCriticalSection(&mtx.critical_section);
|
|
||||||
|
|
||||||
if (!success) return false;
|
|
||||||
if (!mtx.recursive)
|
|
||||||
{
|
{
|
||||||
if (mtx.locks)
|
if (!mtx.recursive) return false;
|
||||||
{
|
mtx.locks++;
|
||||||
if (mtx.timed)
|
return true;
|
||||||
{
|
|
||||||
win32::releaseMutex(mtx.handle);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
win32::leaveCriticalSection(&mtx.critical_section);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
mtx.locks++;
|
|
||||||
|
if (!win32::tryAcquireSRWLockExclusive(&mtx.srw_lock)) return false;
|
||||||
|
|
||||||
|
mtx.owner_thread = current_thread;
|
||||||
|
mtx.locks = 1;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<*
|
||||||
|
@require mtx.initialized : "Mutex was not initialized"
|
||||||
|
*>
|
||||||
fn void? NativeMutex.unlock(&mtx)
|
fn void? NativeMutex.unlock(&mtx)
|
||||||
{
|
{
|
||||||
if (!mtx.locks) return thread::UNLOCK_FAILED?;
|
$if env::COMPILER_SAFE_MODE:
|
||||||
mtx.locks--;
|
if (mtx.owner_thread != win32::getCurrentThreadId()) return thread::UNLOCK_FAILED?; // Mutex was not locked by the current thread
|
||||||
if (!mtx.timed)
|
$endif
|
||||||
|
if (--mtx.locks == 0)
|
||||||
{
|
{
|
||||||
win32::leaveCriticalSection(&mtx.critical_section);
|
mtx.owner_thread = 0;
|
||||||
return;
|
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)
|
fn void? NativeConditionVariable.init(&cond)
|
||||||
{
|
{
|
||||||
cond.waiters_count = 0;
|
cond.cond_var = {};
|
||||||
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?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void? NativeConditionVariable.destroy(&cond) @maydiscard
|
fn void? NativeConditionVariable.destroy(&cond) @maydiscard
|
||||||
{
|
{
|
||||||
if (cond.event_one) win32::closeHandle(cond.event_one);
|
// Nothing to do
|
||||||
if (cond.event_all) win32::closeHandle(cond.event_all);
|
|
||||||
win32::deleteCriticalSection(&cond.waiters_count_lock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void? NativeConditionVariable.signal(&cond)
|
fn void? NativeConditionVariable.signal(&cond)
|
||||||
{
|
{
|
||||||
win32::enterCriticalSection(&cond.waiters_count_lock);
|
win32::wakeConditionVariable(&cond.cond_var);
|
||||||
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?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void? NativeConditionVariable.broadcast(&cond)
|
fn void? NativeConditionVariable.broadcast(&cond)
|
||||||
{
|
{
|
||||||
win32::enterCriticalSection(&cond.waiters_count_lock);
|
win32::wakeAllConditionVariable(&cond.cond_var);
|
||||||
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?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void? timedwait(NativeConditionVariable* cond, NativeMutex* mtx, uint timeout) @private
|
fn void? timedwait(NativeConditionVariable* cond, NativeMutex* mtx, uint timeout) @private
|
||||||
{
|
{
|
||||||
win32::enterCriticalSection(&cond.waiters_count_lock);
|
Win32_DWORD owner_thread = mtx.owner_thread;
|
||||||
cond.waiters_count++;
|
if (mtx.locks != 1 || owner_thread != win32::getCurrentThreadId()) return thread::WAIT_FAILED?;
|
||||||
win32::leaveCriticalSection(&cond.waiters_count_lock);
|
mtx.owner_thread = 0;
|
||||||
|
mtx.locks = 0;
|
||||||
mtx.unlock()!;
|
defer
|
||||||
|
|
||||||
uint result = win32::waitForMultipleObjects(2, &cond.events, 0, timeout);
|
|
||||||
switch (result)
|
|
||||||
{
|
{
|
||||||
case win32::WAIT_TIMEOUT:
|
mtx.owner_thread = owner_thread;
|
||||||
mtx.lock()!;
|
mtx.locks = 1;
|
||||||
return thread::WAIT_TIMEOUT?;
|
|
||||||
case win32::WAIT_FAILED:
|
|
||||||
mtx.lock()!;
|
|
||||||
return thread::WAIT_FAILED?;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
if (!win32::sleepConditionVariableSRW(&cond.cond_var, &mtx.srw_lock, timeout, 0))
|
||||||
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::resetEvent(cond.event_all))
|
if (win32::getLastError() == win32::ERROR_TIMEOUT)
|
||||||
{
|
{
|
||||||
mtx.lock()!;
|
return thread::WAIT_TIMEOUT?;
|
||||||
return thread::WAIT_FAILED?;
|
|
||||||
}
|
}
|
||||||
|
return thread::WAIT_FAILED?;
|
||||||
}
|
}
|
||||||
|
|
||||||
mtx.lock()!;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<*
|
||||||
|
@require mtx.initialized : "Mutex was not initialized"
|
||||||
|
*>
|
||||||
fn void? NativeConditionVariable.wait(&cond, NativeMutex* mtx) @inline
|
fn void? NativeConditionVariable.wait(&cond, NativeMutex* mtx) @inline
|
||||||
{
|
{
|
||||||
return timedwait(cond, mtx, win32::INFINITE) @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
|
fn void? NativeConditionVariable.wait_timeout(&cond, NativeMutex* mtx, ulong ms) @inline
|
||||||
{
|
{
|
||||||
if (ms > uint.max) ms = uint.max;
|
if (ms > uint.max) ms = uint.max;
|
||||||
@@ -267,30 +341,12 @@ fn void native_thread_yield()
|
|||||||
|
|
||||||
fn void NativeOnceFlag.call_once(&flag, OnceFn func)
|
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))
|
((OnceFn)parameter)();
|
||||||
{
|
return 1;
|
||||||
case 0:
|
};
|
||||||
if (mem::compare_exchange_volatile(&flag.status, 1, 0, AtomicOrdering.SEQ_CONSISTENT, AtomicOrdering.SEQ_CONSISTENT) == 0)
|
win32::initOnceExecuteOnce(&flag.init_once, callback, func, null);
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn int? NativeThread.join(thread)
|
fn int? NativeThread.join(thread)
|
||||||
|
|||||||
@@ -2,16 +2,16 @@ module std::thread;
|
|||||||
import std::thread::os;
|
import std::thread::os;
|
||||||
import std::time;
|
import std::time;
|
||||||
|
|
||||||
typedef MutexType = int;
|
bitstruct MutexType : int
|
||||||
|
{
|
||||||
const MutexType MUTEX_PLAIN = 0;
|
bool timed;
|
||||||
const MutexType MUTEX_TIMED = 1;
|
bool recursive;
|
||||||
const MutexType MUTEX_RECURSIVE = 2;
|
}
|
||||||
|
|
||||||
typedef Mutex = NativeMutex;
|
typedef Mutex = NativeMutex;
|
||||||
typedef TimedMutex = inline Mutex;
|
|
||||||
typedef RecursiveMutex = inline Mutex;
|
typedef RecursiveMutex = inline Mutex;
|
||||||
typedef TimedRecursiveMutex = inline Mutex;
|
typedef TimedMutex = NativeTimedMutex;
|
||||||
|
typedef TimedRecursiveMutex = inline TimedMutex;
|
||||||
typedef ConditionVariable = NativeConditionVariable;
|
typedef ConditionVariable = NativeConditionVariable;
|
||||||
typedef Thread = inline NativeThread;
|
typedef Thread = inline NativeThread;
|
||||||
typedef OnceFlag = NativeOnceFlag;
|
typedef OnceFlag = NativeOnceFlag;
|
||||||
@@ -33,16 +33,21 @@ faultdef
|
|||||||
INTERRUPTED,
|
INTERRUPTED,
|
||||||
CHANNEL_CLOSED;
|
CHANNEL_CLOSED;
|
||||||
|
|
||||||
macro void? Mutex.init(&mutex) => NativeMutex.init((NativeMutex*)mutex, MUTEX_PLAIN);
|
macro void? Mutex.init(&mutex) => NativeMutex.init((NativeMutex*)mutex, {});
|
||||||
macro void? TimedMutex.init(&mutex) => NativeMutex.init((NativeMutex*)mutex, MUTEX_TIMED);
|
macro void? RecursiveMutex.init(&mutex) => NativeMutex.init((NativeMutex*)mutex, {.recursive});
|
||||||
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.destroy(&mutex) => NativeMutex.destroy((NativeMutex*)mutex);
|
macro void? Mutex.destroy(&mutex) => NativeMutex.destroy((NativeMutex*)mutex);
|
||||||
macro void? Mutex.lock(&mutex) => NativeMutex.lock((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 bool Mutex.try_lock(&mutex) => NativeMutex.try_lock((NativeMutex*)mutex);
|
||||||
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 void? Mutex.unlock(&mutex) => NativeMutex.unlock((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)
|
macro void Mutex.@in_lock(&mutex; @body)
|
||||||
{
|
{
|
||||||
(void)mutex.lock();
|
(void)mutex.lock();
|
||||||
|
|||||||
@@ -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`).
|
- 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`.
|
- Added `Enum.lookup` and `Enum.lookup_field`.
|
||||||
- `c3c build` picks first target rather than the first executable #2105.
|
- `c3c build` picks first target rather than the first executable #2105.
|
||||||
|
- New Win32 Mutex, ConditionVariable and OnceFlag implementation
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
- Trying to cast an enum to int and back caused the compiler to crash.
|
- 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
|
- `@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
|
- 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.
|
- Fixed enum regression after 0.7.0 enum change.
|
||||||
|
- ConditionVariable now properly works on Win32
|
||||||
|
|
||||||
### Stdlib changes
|
### Stdlib changes
|
||||||
- Hash functions for integer vectors and arrays.
|
- Hash functions for integer vectors and arrays.
|
||||||
|
|||||||
Reference in New Issue
Block a user