From 56a52a1d06346edaee316e6d53c4cbd6a07e1056 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 27 Jun 2020 12:21:31 +0100 Subject: [PATCH] Add `sync.condition_wait_for_timeout` for unix --- core/sync/sync_unix.odin | 65 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/core/sync/sync_unix.odin b/core/sync/sync_unix.odin index dd8901872..0a9226e0f 100644 --- a/core/sync/sync_unix.odin +++ b/core/sync/sync_unix.odin @@ -2,6 +2,7 @@ package sync import "core:sys/unix" +import "core:time" // A recursive lock that can only be held by one thread at once Mutex :: struct { @@ -176,3 +177,67 @@ condition_wait_for :: proc(c: ^Condition) -> bool { } return false; } + +// Wait for the condition to be signalled. +// Does not block if the condition has been signalled and no one +// has waited on it yet. +condition_wait_for_timeout :: proc(c: ^Condition, duration: time.Duration) -> bool { + switch m in c.mutex { + case ^Mutex: + mutex_lock(m); + defer mutex_unlock(m); + // NOTE(tetra): If a thread comes by and steals the flag immediately after the signal occurs, + // the thread that gets signalled and wakes up, discovers that the flag was taken and goes + // back to sleep. + // Though this overall behavior is the most sane, there may be a better way to do this that means that + // the first thread to wait, gets the flag first. + if atomic_swap(&c.flag, false, .Sequentially_Consistent) { + return true; + } + + ns := time.duration_nanoseconds(duration); + timeout: time.TimeSpec; + timeout.tv_sec = ns / 1e9; + timeout.tv_nsec = ns % 1e9; + + for { + if unix.pthread_cond_timedwait(&c.handle, &m.handle, &timeout) != 0 { + return false; + } + if atomic_swap(&c.flag, false, .Sequentially_Consistent) { + return true; + } + } + return false; + + case ^Blocking_Mutex: + blocking_mutex_lock(m); + defer blocking_mutex_unlock(m); + // NOTE(tetra): If a thread comes by and steals the flag immediately after the signal occurs, + // the thread that gets signalled and wakes up, discovers that the flag was taken and goes + // back to sleep. + // Though this overall behavior is the most sane, there may be a better way to do this that means that + // the first thread to wait, gets the flag first. + if atomic_swap(&c.flag, false, .Sequentially_Consistent) { + return true; + } + + ns := time.duration_nanoseconds(duration); + + timeout: time.TimeSpec; + timeout.tv_sec = ns / 1e9; + timeout.tv_nsec = ns % 1e9; + + for { + if unix.pthread_cond_timedwait(&c.handle, &m.handle, &timeout) != 0 { + return false; + } + if atomic_swap(&c.flag, false, .Sequentially_Consistent) { + return true; + } + } + return false; + } + return false; +} +