diff --git a/core/sync/sync2/primitives.odin b/core/sync/sync2/primitives.odin index 843e91d8e..05e069e2a 100644 --- a/core/sync/sync2/primitives.odin +++ b/core/sync/sync2/primitives.odin @@ -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 // diff --git a/core/sync/sync2/sema_internal.odin b/core/sync/sync2/sema_internal.odin new file mode 100644 index 000000000..89992fbed --- /dev/null +++ b/core/sync/sync2/sema_internal.odin @@ -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 + } +} \ No newline at end of file