From 8addf53fb5046a65c6fd0c0d2e7894be60468132 Mon Sep 17 00:00:00 2001 From: Felix Kollmann Date: Tue, 13 Feb 2024 18:51:42 +0100 Subject: [PATCH] Add `timedWait` to `std.Thread.Semaphore` (#18805) * Add `timedWait` to `std.Thread.Semaphore` Add example to documentation of `std.Thread.Semaphore` * Add unit test for thread semaphore timed wait Fix missing try * Change unit test to be simpler * Change `timedWait()` to keep a deadline * Change `timedWait()` to return earlier in some scenarios * Change `timedWait()` to keep a deadline (based on std.Timer) (similar to std.Thread.Futex) --------- Co-authored-by: protty <45520026+kprotty@users.noreply.github.com> --- lib/std/Thread/Semaphore.zig | 50 ++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/lib/std/Thread/Semaphore.zig b/lib/std/Thread/Semaphore.zig index 1b182d4c2a..3253d17a8e 100644 --- a/lib/std/Thread/Semaphore.zig +++ b/lib/std/Thread/Semaphore.zig @@ -1,6 +1,23 @@ //! A semaphore is an unsigned integer that blocks the kernel thread if //! the number would become negative. //! This API supports static initialization and does not require deinitialization. +//! +//! Example: +//! ``` +//! var s = Semaphore{}; +//! +//! fn consumer() void { +//! s.wait(); +//! } +//! +//! fn producer() void { +//! s.post(); +//! } +//! +//! const thread = try std.Thread.spawn(.{}, producer, .{}); +//! consumer(); +//! thread.join(); +//! ``` mutex: Mutex = .{}, cond: Condition = .{}, @@ -26,6 +43,26 @@ pub fn wait(sem: *Semaphore) void { sem.cond.signal(); } +pub fn timedWait(sem: *Semaphore, timeout_ns: u64) error{Timeout}!void { + var timeout_timer = std.time.Timer.start() catch unreachable; + + sem.mutex.lock(); + defer sem.mutex.unlock(); + + while (sem.permits == 0) { + const elapsed = timeout_timer.read(); + if (elapsed > timeout_ns) + return error.Timeout; + + const local_timeout_ns = timeout_ns - elapsed; + try sem.cond.timedWait(&sem.mutex, local_timeout_ns); + } + + sem.permits -= 1; + if (sem.permits > 0) + sem.cond.signal(); +} + pub fn post(sem: *Semaphore) void { sem.mutex.lock(); defer sem.mutex.unlock(); @@ -59,3 +96,16 @@ test "Thread.Semaphore" { sem.wait(); try testing.expect(n == num_threads); } + +test "Thread.Semaphore - timedWait" { + var sem = Semaphore{}; + try testing.expectEqual(0, sem.permits); + + try testing.expectError(error.Timeout, sem.timedWait(1)); + + sem.post(); + try testing.expectEqual(1, sem.permits); + + try sem.timedWait(1); + try testing.expectEqual(0, sem.permits); +}