Browse Source

Add `sync.Parker`

gingerBill 3 years ago
parent
commit
78a8da5fea
1 changed files with 56 additions and 0 deletions
  1. 56 0
      core/sync/extended.odin

+ 56 - 0
core/sync/extended.odin

@@ -297,3 +297,59 @@ once_do :: proc(o: ^Once, fn: proc()) {
 		do_slow(o, fn)
 	}
 }
+
+
+
+// A Parker is an associated token which is initially not present:
+//     * The `park` procedure blocks the current thread unless or until the token
+//       is available, at which point the token is consumed.
+//     * The `park_with_timeout` procedures works the same as `park` but only
+//       blocks for the specified duration.
+//     * The `unpark` procedure automatically makes the token available if it
+//       was not already.
+Parker :: struct {
+	state: Futex,
+}
+
+// Blocks the current thread until the token is made available.
+//
+// Assumes this is only called by the thread that owns the Parker.
+park :: proc(p: ^Parker) {
+	EMPTY    :: 0
+	NOTIFIED :: 1
+	PARKED   :: max(u32)
+	if atomic_sub_explicit(&p.state, 1, .Acquire) == NOTIFIED {
+		return
+	}
+	for {
+		futex_wait(&p.state, PARKED)
+		if _, ok := atomic_compare_exchange_strong_explicit(&p.state, NOTIFIED, EMPTY, .Acquire, .Acquire); ok {
+			return
+		}
+	}
+}
+
+// Blocks the current thread until the token is made available, but only
+// for a limited duration.
+//
+// Assumes this is only called by the thread that owns the Parker
+park_with_timeout :: proc(p: ^Parker, duration: time.Duration) {
+	EMPTY    :: 0
+	NOTIFIED :: 1
+	PARKED   :: max(u32)
+	if atomic_sub_explicit(&p.state, 1, .Acquire) == NOTIFIED {
+		return
+	}
+	futex_wait_with_timeout(&p.state, PARKED, duration)
+	atomic_exchange_explicit(&p.state, EMPTY, .Acquire)
+}
+
+// Automatically makes thee token available if it was not already.
+unpark :: proc(p: ^Parker)  {
+	EMPTY    :: 0
+	NOTIFIED :: 1
+	PARKED   :: max(Futex)
+	if atomic_exchange_explicit(&p.state, NOTIFIED, .Release) == PARKED {
+		futex_signal(&p.state)
+	}
+}