From 33b5dbb82c3c68596ce41abcea6aea3834c0d3a7 Mon Sep 17 00:00:00 2001 From: Nathan Michaels Date: Thu, 19 Dec 2019 23:42:27 -0500 Subject: [PATCH 1/2] Document std.Mutex. Not sure what the build platform is for the generated documentation, and it's worth thinking about how best to deal with this pattern. It might be worth figuring out how to rewrite this to have a single definition of the public API with the implementation chosen at compile time. --- lib/std/mutex.zig | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/lib/std/mutex.zig b/lib/std/mutex.zig index 26f8e29dacd4cbaa2419250eca833a574cdfc0fd..c6fe45db5e435a16775b466ec676763d189aecf5 100644 --- a/lib/std/mutex.zig +++ b/lib/std/mutex.zig @@ -5,11 +5,28 @@ const testing = std.testing; const SpinLock = std.SpinLock; const ResetEvent = std.ResetEvent; -/// Lock may be held only once. If the same thread -/// tries to acquire the same mutex twice, it deadlocks. -/// This type supports static initialization and is at most `@sizeOf(usize)` in size. -/// When an application is built in single threaded release mode, all the functions are -/// no-ops. In single threaded debug mode, there is deadlock detection. +/// Lock may be held only once. If the same thread tries to acquire +/// the same mutex twice, it deadlocks. This type supports static +/// initialization and is at most `@sizeOf(usize)` in size. When an +/// application is built in single threaded release mode, all the +/// functions are no-ops. In single threaded debug mode, there is +/// deadlock detection. +/// +/// Example usage: +/// var m = Mutex.init(); +/// defer m.deinit(); +/// +/// const lock = m.acquire(); +/// defer lock.release(); +/// ... critical code +/// +/// Non-blocking: +/// if (m.tryAcquire) |lock| { +/// defer lock.release(); +/// // ... critical section +/// } else { +/// // ... lock not acquired +/// } pub const Mutex = if (builtin.single_threaded) struct { lock: @TypeOf(lock_init), @@ -26,14 +43,21 @@ pub const Mutex = if (builtin.single_threaded) } }; + /// Create a new mutex in unlocked state. pub fn init() Mutex { return Mutex{ .lock = lock_init }; } + /// Free a mutex created with init. Calling this while the + /// mutex is held may result in safety-checked undefined + /// behavior. pub fn deinit(self: *Mutex) void { self.* = undefined; } + /// Try to acquire the mutex without blocking. Returns null if + /// the mutex is unavailable. Otherwise returns Held. Call + /// release on Held. pub fn tryAcquire(self: *Mutex) ?Held { if (std.debug.runtime_safety) { if (self.lock) return null; @@ -42,6 +66,8 @@ pub const Mutex = if (builtin.single_threaded) return Held{ .mutex = self }; } + /// Acquire the mutex. Will deadlock if the mutex is already + /// held by the calling thread. pub fn acquire(self: *Mutex) Held { return self.tryAcquire() orelse @panic("deadlock detected"); } @@ -200,9 +226,12 @@ else if (builtin.link_libc or builtin.os == .linux) } } + /// Returned when the lock is acquired. Call release to + /// release. pub const Held = struct { mutex: *Mutex, + /// Release the held lock. pub fn release(self: Held) void { // first, remove the lock bit so another possibly parallel acquire() can succeed. // use .Sub since it can be usually compiled down more efficiency -- 2.54.0 From 45339aec02124b8ae9480eeb69fd6cd5bd2ee5aa Mon Sep 17 00:00:00 2001 From: Nathan Michaels Date: Sat, 21 Dec 2019 01:30:55 -0500 Subject: [PATCH 2/2] Fix wording on deinit. --- lib/std/mutex.zig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/std/mutex.zig b/lib/std/mutex.zig index c6fe45db5e435a16775b466ec676763d189aecf5..8e2cdb3c766d0b7d9b46174876f36d1ef566f15b 100644 --- a/lib/std/mutex.zig +++ b/lib/std/mutex.zig @@ -49,8 +49,7 @@ pub const Mutex = if (builtin.single_threaded) } /// Free a mutex created with init. Calling this while the - /// mutex is held may result in safety-checked undefined - /// behavior. + /// mutex is held is illegal behavior. pub fn deinit(self: *Mutex) void { self.* = undefined; } -- 2.54.0