Browse Source

Add `sync.condition_wait_for_timeout` for unix

gingerBill 5 years ago
parent
commit
56a52a1d06
1 changed files with 65 additions and 0 deletions
  1. 65 0
      core/sync/sync_unix.odin

+ 65 - 0
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;
+}
+