Browse Source

Implement sync2.Recursive_Mutex using `WaitOnAddress` and friends on Windows

gingerBill 4 years ago
parent
commit
17390cd317

+ 4 - 37
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);
 }
 
 

+ 49 - 0
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,

+ 48 - 0
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,
 }

+ 50 - 0
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,