Implement sync2.Recursive_Mutex using WaitOnAddress and friends on Windows

This commit is contained in:
gingerBill
2021-04-27 17:19:12 +01:00
parent 7ac80544a1
commit 17390cd317
4 changed files with 151 additions and 37 deletions
+4 -37
View File
@@ -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);
}
+49
View File
@@ -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,
+48
View File
@@ -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,
}
+50
View File
@@ -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,