| ... | ... | @@ -30,7 +30,13 @@ pub const init: Recursive = .{ |
| 30 | 30 | /// Otherwise, returns `true` and the caller should `unlock()` the Mutex to release it. |
| 31 | 31 | pub fn tryLock(r: *Recursive) bool { |
| 32 | 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 | } |
| 35 | 41 | |
| 36 | 42 | /// Acquires the `Mutex`, blocking the current thread while the mutex is |
| ... | ... | @@ -42,12 +48,12 @@ pub fn tryLock(r: *Recursive) bool { |
| 42 | 48 | /// of whether the lock was already held by the same thread. |
| 43 | 49 | pub fn lock(r: *Recursive) void { |
| 44 | 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 | 52 | r.mutex.lock(); |
| 47 | 53 | assert(r.lock_count == 0); |
| 48 | | r.lock_count = 1; |
| 49 | | @atomicStore(std.Thread.Id, &r.thread_id, current_thread_id, .monotonic); |
| 54 | @atomicStore(std.Thread.Id, &r.thread_id, current_thread_id, .unordered); |
| 50 | 55 | } |
| 56 | r.lock_count += 1; |
| 51 | 57 | } |
| 52 | 58 | |
| 53 | 59 | /// Releases the `Mutex` which was previously acquired with `lock` or `tryLock`. |
| ... | ... | @@ -57,30 +63,10 @@ pub fn lock(r: *Recursive) void { |
| 57 | 63 | pub fn unlock(r: *Recursive) void { |
| 58 | 64 | r.lock_count -= 1; |
| 59 | 65 | if (r.lock_count == 0) { |
| 60 | | // Prevent race where: |
| 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); |
| 66 | @atomicStore(std.Thread.Id, &r.thread_id, invalid_thread_id, .unordered); |
| 64 | 67 | r.mutex.unlock(); |
| 65 | 68 | } |
| 66 | 69 | } |
| 67 | 70 | |
| 68 | | fn 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 | 71 | /// A value that does not alias any other thread id. |
| 86 | | const invalid_thread_id: std.Thread.Id = 0; |
| 72 | const invalid_thread_id: std.Thread.Id = std.math.maxInt(std.Thread.Id); |