| ... | @@ -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. |
| 31 | pub fn tryLock(r: *Recursive) bool { | 31 | pub 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 | } |
| 35 | | 41 | |
| 36 | /// Acquires the `Mutex`, blocking the current thread while the mutex is | 42 | /// 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. |
| 43 | pub fn lock(r: *Recursive) void { | 49 | pub 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 | } |
| 52 | | 58 | |
| 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 { |
| 57 | pub fn unlock(r: *Recursive) void { | 63 | pub 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 | } |
| 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 | /// A value that does not alias any other thread id. | 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); |