Separate Sema implementation from "header"; Allow for either Futex or Mutex+Cond implementations of Sema

This commit is contained in:
gingerBill
2021-10-11 19:48:27 +01:00
parent d97c6a7657
commit 077bf28d26
2 changed files with 127 additions and 46 deletions
+11 -46
View File
@@ -174,57 +174,22 @@ cond_broadcast :: proc(c: ^Cond) {
//
// A Sema must not be copied after first use
Sema :: struct {
count: Futex,
}
sema_wait :: proc(s: ^Sema) {
for {
original_count := atomic_load(&s.count)
for original_count == 0 {
futex_wait(&s.count, u32(original_count))
original_count = s.count
}
if original_count == atomic_compare_exchange_strong(&s.count, original_count-1, original_count) {
return
}
}
}
sema_wait_with_timeout :: proc(s: ^Sema, duration: time.Duration) -> bool {
if duration <= 0 {
return false
}
start := time.tick_now()
for {
original_count := atomic_load(&s.count)
remaining := duration - time.tick_since(start)
if remaining < 0 {
return false
}
for original_count == 0 {
ok := futex_wait_with_timeout(&s.count, u32(original_count), remaining)
if !ok {
return false
}
original_count = s.count
}
if original_count == atomic_compare_exchange_strong(&s.count, original_count-1, original_count) {
return true
}
}
impl: _Sema,
}
sema_post :: proc(s: ^Sema, count := 1) {
atomic_add(&s.count, Futex(count))
if count == 1 {
futex_signal(&s.count)
} else {
futex_broadcast(&s.count)
}
_sema_post(s, count)
}
sema_wait :: proc(s: ^Sema) {
_sema_wait(s)
}
sema_wait_with_timeout :: proc(s: ^Sema, duration: time.Duration) -> bool {
return _sema_wait_with_timeout(s, duration)
}
// Futex is a fast userspace mutual exclusion lock, using a 32-bit memory address as a hint
//
+116
View File
@@ -0,0 +1,116 @@
package sync2
import "core:time"
when #config(ODIN_SYNC_SEMA_USE_FUTEX, true) {
_Sema :: struct {
count: Futex,
}
_sema_post :: proc(s: ^Sema, count := 1) {
atomic_add(&s.impl.count, Futex(count))
if count == 1 {
futex_signal(&s.impl.count)
} else {
futex_broadcast(&s.impl.count)
}
}
_sema_wait :: proc(s: ^Sema) {
for {
original_count := atomic_load(&s.impl.count)
for original_count == 0 {
futex_wait(&s.impl.count, u32(original_count))
original_count = s.impl.count
}
if original_count == atomic_compare_exchange_strong(&s.impl.count, original_count-1, original_count) {
return
}
}
}
_sema_wait_with_timeout :: proc(s: ^Sema, duration: time.Duration) -> bool {
if duration <= 0 {
return false
}
for {
original_count := atomic_load(&s.impl.count)
for start := time.tick_now(); original_count == 0; /**/ {
remaining := duration - time.tick_since(start)
if remaining < 0 {
return false
}
if !futex_wait_with_timeout(&s.impl.count, u32(original_count), remaining) {
return false
}
original_count = s.impl.count
}
if original_count == atomic_compare_exchange_strong(&s.impl.count, original_count-1, original_count) {
return true
}
}
}
} else {
_Sema :: struct {
mutex: Mutex,
cond: Cond,
count: i32,
}
_sema_post :: proc(s: ^Sema, count := 1) {
mutex_lock(&s.impl.mutex)
defer mutex_unlock(&s.impl.mutex)
s.impl.count += i32(count)
if count == 1 {
cond_signal(&s.impl.cond)
} else {
cond_broadcast(&s.impl.cond)
}
}
_sema_wait :: proc(s: ^Sema) {
mutex_lock(&s.impl.mutex)
defer mutex_unlock(&s.impl.mutex)
for s.impl.count == 0 {
cond_wait(&s.impl.cond, &s.impl.mutex)
}
s.impl.count -= 1
if s.impl.count > 0 {
cond_signal(&s.impl.cond)
}
}
_sema_wait_with_timeout :: proc(s: ^Sema, duration: time.Duration) -> bool {
if duration <= 0 {
return false
}
mutex_lock(&s.impl.mutex)
defer mutex_unlock(&s.impl.mutex)
start := time.tick_now()
for s.impl.count == 0 {
remaining := duration - time.tick_since(start)
if remaining < 0 {
return false
}
if !cond_wait_with_timeout(&s.impl.cond, &s.impl.mutex, remaining) {
return false
}
}
s.impl.count -= 1
if s.impl.count > 0 {
cond_signal(&s.impl.cond)
}
return true
}
}