From 17390cd317192345d7c2a03eefb4af2032705753 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 27 Apr 2021 17:19:12 +0100 Subject: [PATCH] Implement sync2.Recursive_Mutex using `WaitOnAddress` and friends on Windows --- core/sync/sync2/primitives.odin | 41 ++----------------- core/sync/sync2/primitives_atomic.odin | 49 +++++++++++++++++++++++ core/sync/sync2/primitives_pthreads.odin | 48 +++++++++++++++++++++++ core/sync/sync2/primitives_windows.odin | 50 ++++++++++++++++++++++++ 4 files changed, 151 insertions(+), 37 deletions(-) diff --git a/core/sync/sync2/primitives.odin b/core/sync/sync2/primitives.odin index e7c29afc8..07ac91515 100644 --- a/core/sync/sync2/primitives.odin +++ b/core/sync/sync2/primitives.odin @@ -72,52 +72,19 @@ rw_mutex_try_shared_lock :: proc(rw: ^RW_Mutex) -> bool { // // A Recursive_Mutex must not be copied after first use Recursive_Mutex :: struct { - // TODO(bill): Is this implementation too lazy? - // Can this be made to work on all OSes without construction and destruction, i.e. Zero is Initialized - // CRITICAL_SECTION would be a perfect candidate for this on Windows but that cannot be "dumb" - - owner: int, - recursion: int, - mutex: Mutex, + impl: _Recursive_Mutex, } recursive_mutex_lock :: proc(m: ^Recursive_Mutex) { - tid := runtime.current_thread_id(); - if tid != m.owner { - mutex_lock(&m.mutex); - } - // inside the lock - m.owner = tid; - m.recursion += 1; + _recursive_mutex_lock(m); } recursive_mutex_unlock :: proc(m: ^Recursive_Mutex) { - tid := runtime.current_thread_id(); - assert(tid == m.owner); - m.recursion -= 1; - recursion := m.recursion; - if recursion == 0 { - m.owner = 0; - } - if recursion == 0 { - mutex_unlock(&m.mutex); - } - // outside the lock - + _recursive_mutex_unlock(m); } recursive_mutex_try_lock :: proc(m: ^Recursive_Mutex) -> bool { - tid := runtime.current_thread_id(); - if m.owner == tid { - return mutex_try_lock(&m.mutex); - } - if !mutex_try_lock(&m.mutex) { - return false; - } - // inside the lock - m.owner = tid; - m.recursion += 1; - return true; + return _recursive_mutex_try_lock(m); } diff --git a/core/sync/sync2/primitives_atomic.odin b/core/sync/sync2/primitives_atomic.odin index 0d6b14987..7043f8c84 100644 --- a/core/sync/sync2/primitives_atomic.odin +++ b/core/sync/sync2/primitives_atomic.odin @@ -5,6 +5,7 @@ package sync2 when !#config(ODIN_SYNC_USE_PTHREADS, true) { import "core:time" +import "core:runtime" _Mutex_State :: enum i32 { Unlocked = 0, @@ -160,6 +161,54 @@ _rw_mutex_try_shared_lock :: proc(rw: ^RW_Mutex) -> bool { } +_Recursive_Mutex :: struct { + owner: int, + recursion: int, + mutex: Mutex, +} + +_recursive_mutex_lock :: proc(m: ^Recursive_Mutex) { + tid := runtime.current_thread_id(); + if tid != m.impl.owner { + mutex_lock(&m.impl.mutex); + } + // inside the lock + m.impl.owner = tid; + m.impl.recursion += 1; +} + +_recursive_mutex_unlock :: proc(m: ^Recursive_Mutex) { + tid := runtime.current_thread_id(); + assert(tid == m.impl.owner); + m.impl.recursion -= 1; + recursion := m.impl.recursion; + if recursion == 0 { + m.impl.owner = 0; + } + if recursion == 0 { + mutex_unlock(&m.impl.mutex); + } + // outside the lock + +} + +_recursive_mutex_try_lock :: proc(m: ^Recursive_Mutex) -> bool { + tid := runtime.current_thread_id(); + if m.impl.owner == tid { + return mutex_try_lock(&m.impl.mutex); + } + if !mutex_try_lock(&m.impl.mutex) { + return false; + } + // inside the lock + m.impl.owner = tid; + m.impl.recursion += 1; + return true; +} + + + + Queue_Item :: struct { next: ^Queue_Item, diff --git a/core/sync/sync2/primitives_pthreads.odin b/core/sync/sync2/primitives_pthreads.odin index 17940c991..ea0d140d8 100644 --- a/core/sync/sync2/primitives_pthreads.odin +++ b/core/sync/sync2/primitives_pthreads.odin @@ -5,6 +5,7 @@ package sync2 when #config(ODIN_SYNC_USE_PTHREADS, true) { import "core:time" +import "core:runtime" import "core:sys/unix" _Mutex_State :: enum i32 { @@ -120,6 +121,53 @@ _rw_mutex_try_shared_lock :: proc(rw: ^RW_Mutex) -> bool { return false; } + +_Recursive_Mutex :: struct { + owner: int, + recursion: int, + mutex: Mutex, +} + +_recursive_mutex_lock :: proc(m: ^Recursive_Mutex) { + tid := runtime.current_thread_id(); + if tid != m.impl.owner { + mutex_lock(&m.impl.mutex); + } + // inside the lock + m.impl.owner = tid; + m.impl.recursion += 1; +} + +_recursive_mutex_unlock :: proc(m: ^Recursive_Mutex) { + tid := runtime.current_thread_id(); + assert(tid == m.impl.owner); + m.impl.recursion -= 1; + recursion := m.impl.recursion; + if recursion == 0 { + m.impl.owner = 0; + } + if recursion == 0 { + mutex_unlock(&m.impl.mutex); + } + // outside the lock + +} + +_recursive_mutex_try_lock :: proc(m: ^Recursive_Mutex) -> bool { + tid := runtime.current_thread_id(); + if m.impl.owner == tid { + return mutex_try_lock(&m.impl.mutex); + } + if !mutex_try_lock(&m.impl.mutex) { + return false; + } + // inside the lock + m.impl.owner = tid; + m.impl.recursion += 1; + return true; +} + + _Cond :: struct { pthread_cond: unix.pthread_cond_t, } diff --git a/core/sync/sync2/primitives_windows.odin b/core/sync/sync2/primitives_windows.odin index 0eb6579a7..a66b4a9bd 100644 --- a/core/sync/sync2/primitives_windows.odin +++ b/core/sync/sync2/primitives_windows.odin @@ -50,6 +50,56 @@ _rw_mutex_try_shared_lock :: proc(rw: ^RW_Mutex) -> bool { } +_Recursive_Mutex :: struct { + owner: u32, + claim_count: i32, +} + +_recursive_mutex_lock :: proc(m: ^Recursive_Mutex) { + tid := win32.GetCurrentThreadId(); + for { + prev_owner := atomic_cxchg_acq(&m.impl.owner, tid, 0); + switch prev_owner { + case 0, tid: + m.impl.claim_count += 1; + // inside the lock + return; + } + + win32.WaitOnAddress( + &m.impl.owner, + &prev_owner, + size_of(prev_owner), + win32.INFINITE, + ); + } +} + +_recursive_mutex_unlock :: proc(m: ^Recursive_Mutex) { + m.impl.claim_count -= 1; + if m.impl.claim_count != 0 { + return; + } + atomic_xchg_rel(&m.impl.owner, 0); + win32.WakeByAddressSingle(&m.impl.owner); + // outside the lock + +} + +_recursive_mutex_try_lock :: proc(m: ^Recursive_Mutex) -> bool { + tid := win32.GetCurrentThreadId(); + prev_owner := atomic_cxchg_acq(&m.impl.owner, tid, 0); + switch prev_owner { + case 0, tid: + m.impl.claim_count += 1; + // inside the lock + return true; + } + return false; +} + + + _Cond :: struct { cond: win32.CONDITION_VARIABLE,