authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-06-12 18:07:39-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-06-12 18:07:39-07:00
log5fc1f8a32bb7d66c0db04e497b89f7e33f408722
treeffbd491bd4ef97b9169dcfc89ba3c14b60cc03ef
parentfad223d92ed34caac163695cc2a32cba80267c32

std.Thread.Mutex.Recursive: alternate implementation

This version is simpler. Thanks King!

1 files changed, 12 insertions(+), 26 deletions(-)

lib/std/Thread/Mutex/Recursive.zig+12-26
...@@ -30,7 +30,13 @@ pub const init: Recursive = .{...@@ -30,7 +30,13 @@ pub const init: Recursive = .{
30/// Otherwise, returns `true` and the caller should `unlock()` the Mutex to release it.30/// Otherwise, returns `true` and the caller should `unlock()` the Mutex to release it.
31pub fn tryLock(r: *Recursive) bool {31pub fn tryLock(r: *Recursive) bool {
32 const current_thread_id = std.Thread.getCurrentId();32 const current_thread_id = std.Thread.getCurrentId();
33 return tryLockInner(r, current_thread_id);33 if (@atomicLoad(std.Thread.Id, &r.thread_id, .unordered) != current_thread_id) {
34 if (!r.mutex.tryLock()) return false;
35 assert(r.lock_count == 0);
36 @atomicStore(std.Thread.Id, &r.thread_id, current_thread_id, .unordered);
37 }
38 r.lock_count += 1;
39 return true;
34}40}
3541
36/// Acquires the `Mutex`, blocking the current thread while the mutex is42/// Acquires the `Mutex`, blocking the current thread while the mutex is
...@@ -42,12 +48,12 @@ pub fn tryLock(r: *Recursive) bool {...@@ -42,12 +48,12 @@ pub fn tryLock(r: *Recursive) bool {
42/// of whether the lock was already held by the same thread.48/// of whether the lock was already held by the same thread.
43pub fn lock(r: *Recursive) void {49pub fn lock(r: *Recursive) void {
44 const current_thread_id = std.Thread.getCurrentId();50 const current_thread_id = std.Thread.getCurrentId();
45 if (!tryLockInner(r, current_thread_id)) {51 if (@atomicLoad(std.Thread.Id, &r.thread_id, .unordered) != current_thread_id) {
46 r.mutex.lock();52 r.mutex.lock();
47 assert(r.lock_count == 0);53 assert(r.lock_count == 0);
48 r.lock_count = 1;54 @atomicStore(std.Thread.Id, &r.thread_id, current_thread_id, .unordered);
49 @atomicStore(std.Thread.Id, &r.thread_id, current_thread_id, .monotonic);
50 }55 }
56 r.lock_count += 1;
51}57}
5258
53/// Releases the `Mutex` which was previously acquired with `lock` or `tryLock`.59/// Releases the `Mutex` which was previously acquired with `lock` or `tryLock`.
...@@ -57,30 +63,10 @@ pub fn lock(r: *Recursive) void {...@@ -57,30 +63,10 @@ pub fn lock(r: *Recursive) void {
57pub fn unlock(r: *Recursive) void {63pub fn unlock(r: *Recursive) void {
58 r.lock_count -= 1;64 r.lock_count -= 1;
59 if (r.lock_count == 0) {65 if (r.lock_count == 0) {
60 // Prevent race where:66 @atomicStore(std.Thread.Id, &r.thread_id, invalid_thread_id, .unordered);
61 // * Thread A obtains lock and has not yet stored the new thread id.
62 // * Thread B loads the thread id after tryLock() false and observes stale thread id.
63 @atomicStore(std.Thread.Id, &r.thread_id, invalid_thread_id, .seq_cst);
64 r.mutex.unlock();67 r.mutex.unlock();
65 }68 }
66}69}
6770
68fn tryLockInner(r: *Recursive, current_thread_id: std.Thread.Id) bool {
69 if (r.mutex.tryLock()) {
70 assert(r.lock_count == 0);
71 r.lock_count = 1;
72 @atomicStore(std.Thread.Id, &r.thread_id, current_thread_id, .monotonic);
73 return true;
74 }
75
76 const locked_thread_id = @atomicLoad(std.Thread.Id, &r.thread_id, .monotonic);
77 if (locked_thread_id == current_thread_id) {
78 r.lock_count += 1;
79 return true;
80 }
81
82 return false;
83}
84
85/// A value that does not alias any other thread id.71/// A value that does not alias any other thread id.
86const invalid_thread_id: std.Thread.Id = 0;72const invalid_thread_id: std.Thread.Id = std.math.maxInt(std.Thread.Id);