Commit 9698ea3173

Andrew Kelley <andrew@ziglang.org>
2021-01-15 05:28:22
std.Thread.Mutex: restore the "Held" API
so that std.Thread.Mutex.Dummy can be used as a drop in replacement.
1 parent a9667b5
Changed files (2)
lib/std/heap/general_purpose_allocator.zig
@@ -150,12 +150,12 @@ pub const Config = struct {
 
     /// What type of mutex you'd like to use, for thread safety.
     /// when specfied, the mutex type must have the same shape as `std.Thread.Mutex` and
-    /// `std.mutex.Dummy`, and have no required fields. Specifying this field causes
+    /// `std.Thread.Mutex.Dummy`, and have no required fields. Specifying this field causes
     /// the `thread_safe` field to be ignored.
     ///
     /// when null (default):
     /// * the mutex type defaults to `std.Thread.Mutex` when thread_safe is enabled.
-    /// * the mutex type defaults to `std.mutex.Dummy` otherwise.
+    /// * the mutex type defaults to `std.Thread.Mutex.Dummy` otherwise.
     MutexType: ?type = null,
 
     /// This is a temporary debugging trick you can use to turn segfaults into more helpful
@@ -189,7 +189,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
         else if (config.thread_safe)
             std.Thread.Mutex{}
         else
-            std.mutex.Dummy{};
+            std.Thread.Mutex.Dummy{};
 
         const stack_n = config.stack_trace_frames;
         const one_trace_size = @sizeOf(usize) * stack_n;
lib/std/Thread/Mutex.zig
@@ -38,30 +38,16 @@ const linux = os.linux;
 const testing = std.testing;
 const StaticResetEvent = std.thread.StaticResetEvent;
 
-pub const Held = struct {
-    impl: *Impl,
-
-    pub fn release(held: Held) void {
-        held.impl.release();
-    }
-};
-
-/// Try to acquire the mutex without blocking. Returns null if
-/// the mutex is unavailable. Otherwise returns Held. Call
-/// release on Held.
-pub fn tryAcquire(m: *Mutex) ?Held {
-    if (m.impl.tryAcquire()) {
-        return Held{ .impl = &m.impl };
-    } else {
-        return null;
-    }
+/// Try to acquire the mutex without blocking. Returns `null` if the mutex is
+/// unavailable. Otherwise returns `Held`. Call `release` on `Held`.
+pub fn tryAcquire(m: *Mutex) ?Impl.Held {
+    return m.impl.tryAcquire();
 }
 
 /// Acquire the mutex. Deadlocks if the mutex is already
 /// held by the calling thread.
-pub fn acquire(m: *Mutex) Held {
-    m.impl.acquire();
-    return .{ .impl = &m.impl };
+pub fn acquire(m: *Mutex) Impl.Held {
+    return m.impl.acquire();
 }
 
 const Impl = if (builtin.single_threaded)
@@ -82,25 +68,42 @@ pub const AtomicMutex = struct {
         waiting,
     };
 
-    pub fn tryAcquire(self: *AtomicMutex) bool {
-        return @cmpxchgStrong(
+    pub const Held = struct {
+        mutex: *AtomicMutex,
+
+        pub fn release(held: Held) void {
+            switch (@atomicRmw(State, &held.mutex.state, .Xchg, .unlocked, .Release)) {
+                .unlocked => unreachable,
+                .locked => {},
+                .waiting => held.mutex.unlockSlow(),
+            }
+        }
+    };
+
+    pub fn tryAcquire(m: *AtomicMutex) ?Held {
+        if (@cmpxchgStrong(
             State,
-            &self.state,
+            &m.state,
             .unlocked,
             .locked,
             .Acquire,
             .Monotonic,
-        ) == null;
+        ) == null) {
+            return Held{ .mutex = m };
+        } else {
+            return null;
+        }
     }
 
-    pub fn acquire(self: *AtomicMutex) void {
-        switch (@atomicRmw(State, &self.state, .Xchg, .locked, .Acquire)) {
+    pub fn acquire(m: *AtomicMutex) Held {
+        switch (@atomicRmw(State, &m.state, .Xchg, .locked, .Acquire)) {
             .unlocked => {},
-            else => |s| self.lockSlow(s),
+            else => |s| m.lockSlow(s),
         }
+        return Held{ .mutex = m };
     }
 
-    fn lockSlow(self: *AtomicMutex, current_state: State) void {
+    fn lockSlow(m: *AtomicMutex, current_state: State) void {
         @setCold(true);
         var new_state = current_state;
 
@@ -108,7 +111,7 @@ pub const AtomicMutex = struct {
         while (spin < 100) : (spin += 1) {
             const state = @cmpxchgWeak(
                 State,
-                &self.state,
+                &m.state,
                 .unlocked,
                 new_state,
                 .Acquire,
@@ -128,14 +131,14 @@ pub const AtomicMutex = struct {
 
         new_state = .waiting;
         while (true) {
-            switch (@atomicRmw(State, &self.state, .Xchg, new_state, .Acquire)) {
+            switch (@atomicRmw(State, &m.state, .Xchg, new_state, .Acquire)) {
                 .unlocked => return,
                 else => {},
             }
             switch (std.Target.current.os.tag) {
                 .linux => {
                     switch (linux.getErrno(linux.futex_wait(
-                        @ptrCast(*const i32, &self.state),
+                        @ptrCast(*const i32, &m.state),
                         linux.FUTEX_PRIVATE_FLAG | linux.FUTEX_WAIT,
                         @enumToInt(new_state),
                         null,
@@ -151,21 +154,13 @@ pub const AtomicMutex = struct {
         }
     }
 
-    pub fn release(self: *AtomicMutex) void {
-        switch (@atomicRmw(State, &self.state, .Xchg, .unlocked, .Release)) {
-            .unlocked => unreachable,
-            .locked => {},
-            .waiting => self.unlockSlow(),
-        }
-    }
-
-    fn unlockSlow(self: *AtomicMutex) void {
+    fn unlockSlow(m: *AtomicMutex) void {
         @setCold(true);
 
         switch (std.Target.current.os.tag) {
             .linux => {
                 switch (linux.getErrno(linux.futex_wake(
-                    @ptrCast(*const i32, &self.state),
+                    @ptrCast(*const i32, &m.state),
                     linux.FUTEX_PRIVATE_FLAG | linux.FUTEX_WAKE,
                     1,
                 ))) {
@@ -182,18 +177,36 @@ pub const AtomicMutex = struct {
 pub const PthreadMutex = struct {
     pthread_mutex: std.c.pthread_mutex_t = .{},
 
+    pub const Held = struct {
+        mutex: *PthreadMutex,
+
+        pub fn release(held: Held) void {
+            switch (std.c.pthread_mutex_unlock(&held.mutex.pthread_mutex)) {
+                0 => return,
+                std.c.EINVAL => unreachable,
+                std.c.EAGAIN => unreachable,
+                std.c.EPERM => unreachable,
+                else => unreachable,
+            }
+        }
+    };
+
     /// 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: *PthreadMutex) bool {
-        return std.c.pthread_mutex_trylock(&self.pthread_mutex) == 0;
+    pub fn tryAcquire(m: *PthreadMutex) ?Held {
+        if (std.c.pthread_mutex_trylock(&m.pthread_mutex) == 0) {
+            return Held{ .mutex = m };
+        } else {
+            return null;
+        }
     }
 
     /// Acquire the mutex. Will deadlock if the mutex is already
     /// held by the calling thread.
-    pub fn acquire(self: *PthreadMutex) void {
-        switch (std.c.pthread_mutex_lock(&self.pthread_mutex)) {
-            0 => return,
+    pub fn acquire(m: *PthreadMutex) Held {
+        switch (std.c.pthread_mutex_lock(&m.pthread_mutex)) {
+            0 => return Held{ .mutex = m },
             std.c.EINVAL => unreachable,
             std.c.EBUSY => unreachable,
             std.c.EAGAIN => unreachable,
@@ -202,16 +215,6 @@ pub const PthreadMutex = struct {
             else => unreachable,
         }
     }
-
-    pub fn release(self: *PthreadMutex) void {
-        switch (std.c.pthread_mutex_unlock(&self.pthread_mutex)) {
-            0 => return,
-            std.c.EINVAL => unreachable,
-            std.c.EAGAIN => unreachable,
-            std.c.EPERM => unreachable,
-            else => unreachable,
-        }
-    }
 };
 
 /// This has the sematics as `Mutex`, however it does not actually do any
@@ -221,43 +224,56 @@ pub const Dummy = struct {
 
     const lock_init = if (std.debug.runtime_safety) false else {};
 
+    pub const Held = struct {
+        mutex: *Dummy,
+
+        pub fn release(held: Held) void {
+            if (std.debug.runtime_safety) {
+                held.mutex.lock = false;
+            }
+        }
+    };
+
     /// 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: *Dummy) bool {
+    pub fn tryAcquire(m: *Dummy) ?Held {
         if (std.debug.runtime_safety) {
-            if (self.lock) return false;
-            self.lock = true;
+            if (m.lock) return null;
+            m.lock = true;
         }
-        return true;
+        return Held{ .mutex = m };
     }
 
     /// Acquire the mutex. Will deadlock if the mutex is already
     /// held by the calling thread.
-    pub fn acquire(self: *Dummy) void {
-        return self.tryAcquire() orelse @panic("deadlock detected");
-    }
-
-    pub fn release(self: *Dummy) void {
-        if (std.debug.runtime_safety) {
-            self.mutex.lock = false;
-        }
+    pub fn acquire(m: *Dummy) Held {
+        return m.tryAcquire() orelse @panic("deadlock detected");
     }
 };
 
 const WindowsMutex = struct {
     srwlock: windows.SRWLOCK = windows.SRWLOCK_INIT,
 
-    pub fn tryAcquire(self: *WindowsMutex) bool {
-        return TryAcquireSRWLockExclusive(&self.srwlock) != system.FALSE;
-    }
+    pub const Held = struct {
+        mutex: *WindowsMutex,
 
-    pub fn acquire(self: *WindowsMutex) void {
-        AcquireSRWLockExclusive(&self.srwlock);
+        pub fn release(held: Held) void {
+            windows.ReleaseSRWLockExclusive(&held.mutex.srwlock);
+        }
+    };
+
+    pub fn tryAcquire(m: *WindowsMutex) ?Held {
+        if (windows.TryAcquireSRWLockExclusive(&m.srwlock) != windows.FALSE) {
+            return Held{ .mutex = m };
+        } else {
+            return null;
+        }
     }
 
-    pub fn release(self: *WindowsMutex) void {
-        ReleaseSRWLockExclusive(&self.srwlock);
+    pub fn acquire(m: *WindowsMutex) Held {
+        windows.AcquireSRWLockExclusive(&m.srwlock);
+        return Held{ .mutex = m };
     }
 };