diff --git a/lib/std/Progress.zig b/lib/std/Progress.zig index 0625fbaad9..73a4752525 100644 --- a/lib/std/Progress.zig +++ b/lib/std/Progress.zig @@ -56,7 +56,7 @@ done: bool = true, /// Protects the `refresh` function, as well as `node.recently_updated_child`. /// Without this, callsites would call `Node.end` and then free `Node` memory /// while it was still being accessed by the `refresh` function. -update_lock: std.Thread.Mutex = .{}, +update_mutex: std.Thread.Mutex = .{}, /// Keeps track of how many columns in the terminal have been output, so that /// we can move the cursor back later. @@ -103,14 +103,14 @@ pub const Node = struct { self.context.maybeRefresh(); if (self.parent) |parent| { { - const held = self.context.update_lock.acquire(); - defer held.release(); + self.context.update_mutex.lock(); + defer self.context.update_mutex.unlock(); _ = @cmpxchgStrong(?*Node, &parent.recently_updated_child, self, null, .Monotonic, .Monotonic); } parent.completeOne(); } else { - const held = self.context.update_lock.acquire(); - defer held.release(); + self.context.update_mutex.lock(); + defer self.context.update_mutex.unlock(); self.context.done = true; self.context.refreshWithHeldLock(); } @@ -170,8 +170,8 @@ pub fn start(self: *Progress, name: []const u8, estimated_total_items: usize) !* pub fn maybeRefresh(self: *Progress) void { const now = self.timer.read(); if (now < self.initial_delay_ns) return; - const held = self.update_lock.tryAcquire() orelse return; - defer held.release(); + if (!self.update_mutex.tryLock()) return; + defer self.update_mutex.unlock(); // TODO I have observed this to happen sometimes. I think we need to follow Rust's // lead and guarantee monotonically increasing times in the std lib itself. if (now < self.prev_refresh_timestamp) return; @@ -181,8 +181,8 @@ pub fn maybeRefresh(self: *Progress) void { /// Updates the terminal and resets `self.next_refresh_timestamp`. Thread-safe. pub fn refresh(self: *Progress) void { - const held = self.update_lock.tryAcquire() orelse return; - defer held.release(); + if (!self.update_mutex.tryLock()) return; + defer self.update_mutex.unlock(); return self.refreshWithHeldLock(); } diff --git a/lib/std/Thread/Condition.zig b/lib/std/Thread/Condition.zig index 658817fc08..615f68cd9b 100644 --- a/lib/std/Thread/Condition.zig +++ b/lib/std/Thread/Condition.zig @@ -145,8 +145,8 @@ pub const AtomicCondition = struct { var waiter = QueueList.Node{ .data = .{} }; { - const held = cond.queue_mutex.acquire(); - defer held.release(); + cond.queue_mutex.lock(); + defer cond.queue_mutex.unlock(); cond.queue_list.prepend(&waiter); @atomicStore(bool, &cond.pending, true, .SeqCst); @@ -162,8 +162,8 @@ pub const AtomicCondition = struct { return; const maybe_waiter = blk: { - const held = cond.queue_mutex.acquire(); - defer held.release(); + cond.queue_mutex.lock(); + defer cond.queue_mutex.unlock(); const maybe_waiter = cond.queue_list.popFirst(); @atomicStore(bool, &cond.pending, cond.queue_list.first != null, .SeqCst); @@ -181,8 +181,8 @@ pub const AtomicCondition = struct { @atomicStore(bool, &cond.pending, false, .SeqCst); var waiters = blk: { - const held = cond.queue_mutex.acquire(); - defer held.release(); + cond.queue_mutex.lock(); + defer cond.queue_mutex.unlock(); const waiters = cond.queue_list; cond.queue_list = .{}; diff --git a/lib/std/Thread/Mutex.zig b/lib/std/Thread/Mutex.zig index db9a2408b8..c8f5c0534d 100644 --- a/lib/std/Thread/Mutex.zig +++ b/lib/std/Thread/Mutex.zig @@ -8,13 +8,13 @@ //! Example usage: //! var m = Mutex{}; //! -//! const lock = m.acquire(); -//! defer lock.release(); +//! m.lock(); +//! defer m.release(); //! ... critical code //! //! Non-blocking: -//! if (m.tryAcquire) |lock| { -//! defer lock.release(); +//! if (m.tryLock()) { +//! defer m.unlock(); //! // ... critical section //! } else { //! // ... lock not acquired @@ -32,30 +32,22 @@ const linux = os.linux; const testing = std.testing; const StaticResetEvent = std.thread.StaticResetEvent; -/// Try to acquire the mutex without blocking. Returns `null` if the mutex is -/// unavailable. Otherwise returns `Held`. Call `release` on `Held`, or use -/// releaseDirect(). -pub fn tryAcquire(m: *Mutex) ?Held { - return m.impl.tryAcquire(); +/// Try to acquire the mutex without blocking. Returns `false` if the mutex is +/// unavailable. Otherwise returns `true`. Call `unlock` on the mutex to release. +pub fn tryLock(m: *Mutex) bool { + return m.impl.tryLock(); } /// Acquire the mutex. Deadlocks if the mutex is already /// held by the calling thread. -pub fn acquire(m: *Mutex) Held { - return m.impl.acquire(); +pub fn lock(m: *Mutex) void { + m.impl.lock(); } -/// Release the mutex. Prefer Held.release() if available. -pub fn releaseDirect(m: *Mutex) void { - return m.impl.releaseDirect(); +pub fn unlock(m: *Mutex) void { + m.impl.unlock(); } -/// A held mutex handle. Call release to allow other threads to -/// take the mutex. Do not call release() more than once. -/// For more complex scenarios, this handle can be discarded -/// and Mutex.releaseDirect can be called instead. -pub const Held = Impl.Held; - const Impl = if (builtin.single_threaded) Dummy else if (builtin.os.tag == .windows) @@ -65,32 +57,6 @@ else if (std.Thread.use_pthreads) else AtomicMutex; -fn HeldInterface(comptime MutexType: type) type { - return struct { - const Mixin = @This(); - pub const Held = struct { - mutex: *MutexType, - - pub fn release(held: Mixin.Held) void { - held.mutex.releaseDirect(); - } - }; - - pub fn tryAcquire(m: *MutexType) ?Mixin.Held { - if (m.tryAcquireDirect()) { - return Mixin.Held{ .mutex = m }; - } else { - return null; - } - } - - pub fn acquire(m: *MutexType) Mixin.Held { - m.acquireDirect(); - return Mixin.Held{ .mutex = m }; - } - }; -} - pub const AtomicMutex = struct { state: State = .unlocked, @@ -100,9 +66,7 @@ pub const AtomicMutex = struct { waiting, }; - pub usingnamespace HeldInterface(@This()); - - fn tryAcquireDirect(m: *AtomicMutex) bool { + pub fn tryLock(m: *AtomicMutex) bool { return @cmpxchgStrong( State, &m.state, @@ -113,14 +77,14 @@ pub const AtomicMutex = struct { ) == null; } - fn acquireDirect(m: *AtomicMutex) void { + pub fn lock(m: *AtomicMutex) void { switch (@atomicRmw(State, &m.state, .Xchg, .locked, .Acquire)) { .unlocked => {}, else => |s| m.lockSlow(s), } } - fn releaseDirect(m: *AtomicMutex) void { + pub fn unlock(m: *AtomicMutex) void { switch (@atomicRmw(State, &m.state, .Xchg, .unlocked, .Release)) { .unlocked => unreachable, .locked => {}, @@ -202,18 +166,16 @@ pub const AtomicMutex = struct { pub const PthreadMutex = struct { pthread_mutex: std.c.pthread_mutex_t = .{}, - pub usingnamespace HeldInterface(@This()); - /// Try to acquire the mutex without blocking. Returns true if /// the mutex is unavailable. Otherwise returns false. Call /// release when done. - fn tryAcquireDirect(m: *PthreadMutex) bool { + pub fn tryLock(m: *PthreadMutex) bool { return std.c.pthread_mutex_trylock(&m.pthread_mutex) == .SUCCESS; } /// Acquire the mutex. Will deadlock if the mutex is already /// held by the calling thread. - fn acquireDirect(m: *PthreadMutex) void { + pub fn lock(m: *PthreadMutex) void { switch (std.c.pthread_mutex_lock(&m.pthread_mutex)) { .SUCCESS => {}, .INVAL => unreachable, @@ -225,7 +187,7 @@ pub const PthreadMutex = struct { } } - fn releaseDirect(m: *PthreadMutex) void { + pub fn unlock(m: *PthreadMutex) void { switch (std.c.pthread_mutex_unlock(&m.pthread_mutex)) { .SUCCESS => return, .INVAL => unreachable, @@ -239,51 +201,47 @@ pub const PthreadMutex = struct { /// This has the sematics as `Mutex`, however it does not actually do any /// synchronization. Operations are safety-checked no-ops. pub const Dummy = struct { - lock: @TypeOf(lock_init) = lock_init, - - pub usingnamespace HeldInterface(@This()); + locked: @TypeOf(lock_init) = lock_init, const lock_init = if (std.debug.runtime_safety) false else {}; /// Try to acquire the mutex without blocking. Returns false if /// the mutex is unavailable. Otherwise returns true. - fn tryAcquireDirect(m: *Dummy) bool { + pub fn tryLock(m: *Dummy) bool { if (std.debug.runtime_safety) { - if (m.lock) return false; - m.lock = true; + if (m.locked) return false; + m.locked = true; } return true; } /// Acquire the mutex. Will deadlock if the mutex is already /// held by the calling thread. - fn acquireDirect(m: *Dummy) void { - if (!m.tryAcquireDirect()) { + pub fn lock(m: *Dummy) void { + if (!m.tryLock()) { @panic("deadlock detected"); } } - fn releaseDirect(m: *Dummy) void { + pub fn unlock(m: *Dummy) void { if (std.debug.runtime_safety) { - m.lock = false; + m.locked = false; } } }; -const WindowsMutex = struct { +pub const WindowsMutex = struct { srwlock: windows.SRWLOCK = windows.SRWLOCK_INIT, - pub usingnamespace HeldInterface(@This()); - - fn tryAcquireDirect(m: *WindowsMutex) bool { + pub fn tryLock(m: *WindowsMutex) bool { return windows.kernel32.TryAcquireSRWLockExclusive(&m.srwlock) != windows.FALSE; } - fn acquireDirect(m: *WindowsMutex) void { + pub fn lock(m: *WindowsMutex) void { windows.kernel32.AcquireSRWLockExclusive(&m.srwlock); } - fn releaseDirect(m: *WindowsMutex) void { + pub fn unlock(m: *WindowsMutex) void { windows.kernel32.ReleaseSRWLockExclusive(&m.srwlock); } }; @@ -322,8 +280,8 @@ test "basic usage" { fn worker(ctx: *TestContext) void { var i: usize = 0; while (i != TestContext.incr_count) : (i += 1) { - const held = ctx.mutex.acquire(); - defer held.release(); + ctx.mutex.lock(); + defer ctx.mutex.unlock(); ctx.data += 1; } diff --git a/lib/std/Thread/Semaphore.zig b/lib/std/Thread/Semaphore.zig index b5bde88d73..30d16037bc 100644 --- a/lib/std/Thread/Semaphore.zig +++ b/lib/std/Thread/Semaphore.zig @@ -13,8 +13,8 @@ const Mutex = std.Thread.Mutex; const Condition = std.Thread.Condition; pub fn wait(sem: *Semaphore) void { - const held = sem.mutex.acquire(); - defer held.release(); + sem.mutex.lock(); + defer sem.mutex.unlock(); while (sem.permits == 0) sem.cond.wait(&sem.mutex); @@ -25,8 +25,8 @@ pub fn wait(sem: *Semaphore) void { } pub fn post(sem: *Semaphore) void { - const held = sem.mutex.acquire(); - defer held.release(); + sem.mutex.lock(); + defer sem.mutex.unlock(); sem.permits += 1; sem.cond.signal(); diff --git a/lib/std/atomic/queue.zig b/lib/std/atomic/queue.zig index b42d918d06..62de8d9f10 100644 --- a/lib/std/atomic/queue.zig +++ b/lib/std/atomic/queue.zig @@ -31,8 +31,8 @@ pub fn Queue(comptime T: type) type { pub fn put(self: *Self, node: *Node) void { node.next = null; - const held = self.mutex.acquire(); - defer held.release(); + self.mutex.lock(); + defer self.mutex.unlock(); node.prev = self.tail; self.tail = node; @@ -48,8 +48,8 @@ pub fn Queue(comptime T: type) type { /// It is safe to `get()` a node from the queue while another thread tries /// to `remove()` the same node at the same time. pub fn get(self: *Self) ?*Node { - const held = self.mutex.acquire(); - defer held.release(); + self.mutex.lock(); + defer self.mutex.unlock(); const head = self.head orelse return null; self.head = head.next; @@ -67,8 +67,8 @@ pub fn Queue(comptime T: type) type { pub fn unget(self: *Self, node: *Node) void { node.prev = null; - const held = self.mutex.acquire(); - defer held.release(); + self.mutex.lock(); + defer self.mutex.unlock(); const opt_head = self.head; self.head = node; @@ -84,8 +84,8 @@ pub fn Queue(comptime T: type) type { /// It is safe to `remove()` a node from the queue while another thread tries /// to `get()` the same node at the same time. pub fn remove(self: *Self, node: *Node) bool { - const held = self.mutex.acquire(); - defer held.release(); + self.mutex.lock(); + defer self.mutex.unlock(); if (node.prev == null and node.next == null and self.head != node) { return false; @@ -110,8 +110,8 @@ pub fn Queue(comptime T: type) type { /// Note that in a multi-consumer environment a return value of `false` /// does not mean that `get` will yield a non-`null` value! pub fn isEmpty(self: *Self) bool { - const held = self.mutex.acquire(); - defer held.release(); + self.mutex.lock(); + defer self.mutex.unlock(); return self.head == null; } @@ -144,8 +144,8 @@ pub fn Queue(comptime T: type) type { } } }; - const held = self.mutex.acquire(); - defer held.release(); + self.mutex.lock(); + defer self.mutex.unlock(); try stream.print("head: ", .{}); try S.dumpRecursive(stream, self.head, 0, 4); diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 36300e4691..d0344ff00c 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -62,8 +62,8 @@ pub const warn = print; /// Print to stderr, unbuffered, and silently returning on failure. Intended /// for use in "printf debugging." Use `std.log` functions for proper logging. pub fn print(comptime fmt: []const u8, args: anytype) void { - const held = stderr_mutex.acquire(); - defer held.release(); + stderr_mutex.lock(); + defer stderr_mutex.unlock(); const stderr = io.getStdErr().writer(); nosuspend stderr.print(fmt, args) catch return; } @@ -286,8 +286,8 @@ pub fn panicImpl(trace: ?*const std.builtin.StackTrace, first_trace_addr: ?usize // Make sure to release the mutex when done { - const held = panic_mutex.acquire(); - defer held.release(); + panic_mutex.lock(); + defer panic_mutex.unlock(); const stderr = io.getStdErr().writer(); if (builtin.single_threaded) { diff --git a/lib/std/event/lock.zig b/lib/std/event/lock.zig index 645b6adad6..6e095b1890 100644 --- a/lib/std/event/lock.zig +++ b/lib/std/event/lock.zig @@ -32,7 +32,7 @@ pub const Lock = struct { } pub fn acquire(self: *Lock) Held { - const held = self.mutex.acquire(); + self.mutex.lock(); // self.head transitions from multiple stages depending on the value: // UNLOCKED -> LOCKED: @@ -44,7 +44,7 @@ pub const Lock = struct { if (self.head == UNLOCKED) { self.head = LOCKED; - held.release(); + self.mutex.unlock(); return Held{ .lock = self }; } @@ -71,7 +71,7 @@ pub const Lock = struct { .next = undefined, .data = @frame(), }; - held.release(); + self.mutex.unlock(); } return Held{ .lock = self }; @@ -82,8 +82,8 @@ pub const Lock = struct { pub fn release(self: Held) void { const waiter = blk: { - const held = self.lock.mutex.acquire(); - defer held.release(); + self.lock.mutex.lock(); + defer self.lock.mutex.unlock(); // self.head goes through the reverse transition from acquire(): //
->