| ... | ... | @@ -48,6 +48,25 @@ pub fn lockUncancelable(rl: *RwLock, io: Io) void { |
| 48 | 48 | rl.semaphore.waitUncancelable(io); |
| 49 | 49 | } |
| 50 | 50 | |
| 51 | pub fn lock(rl: *RwLock, io: Io) Io.Cancelable!void { |
| 52 | _ = @atomicRmw(usize, &rl.state, .Add, writer, .seq_cst); |
| 53 | rl.mutex.lock(io) catch |err| switch (err) { |
| 54 | error.Canceled => { |
| 55 | _ = @atomicRmw(usize, &rl.state, .Sub, writer, .seq_cst); |
| 56 | return error.Canceled; |
| 57 | }, |
| 58 | }; |
| 59 | |
| 60 | const state = @atomicRmw(usize, &rl.state, .Add, is_writing -% writer, .seq_cst); |
| 61 | if (state & reader_mask != 0) |
| 62 | rl.semaphore.wait(io) catch |err| switch (err) { |
| 63 | error.Canceled => { |
| 64 | rl.unlock(io); |
| 65 | return error.Canceled; |
| 66 | }, |
| 67 | }; |
| 68 | } |
| 69 | |
| 51 | 70 | pub fn unlock(rl: *RwLock, io: Io) void { |
| 52 | 71 | _ = @atomicRmw(usize, &rl.state, .And, ~is_writing, .seq_cst); |
| 53 | 72 | rl.mutex.unlock(io); |
| ... | ... | @@ -93,6 +112,24 @@ pub fn lockSharedUncancelable(rl: *RwLock, io: Io) void { |
| 93 | 112 | rl.mutex.unlock(io); |
| 94 | 113 | } |
| 95 | 114 | |
| 115 | pub fn lockShared(rl: *RwLock, io: Io) Io.Cancelable!void { |
| 116 | var state = @atomicLoad(usize, &rl.state, .seq_cst); |
| 117 | while (state & (is_writing | writer_mask) == 0) { |
| 118 | state = @cmpxchgWeak( |
| 119 | usize, |
| 120 | &rl.state, |
| 121 | state, |
| 122 | state + reader, |
| 123 | .seq_cst, |
| 124 | .seq_cst, |
| 125 | ) orelse return; |
| 126 | } |
| 127 | |
| 128 | try rl.mutex.lock(io); |
| 129 | _ = @atomicRmw(usize, &rl.state, .Add, reader, .seq_cst); |
| 130 | rl.mutex.unlock(io); |
| 131 | } |
| 132 | |
| 96 | 133 | pub fn unlockShared(rl: *RwLock, io: Io) void { |
| 97 | 134 | const state = @atomicRmw(usize, &rl.state, .Sub, reader, .seq_cst); |
| 98 | 135 | |
| ... | ... | @@ -111,6 +148,10 @@ test "internal state" { |
| 111 | 148 | rl.lockUncancelable(io); |
| 112 | 149 | rl.unlock(io); |
| 113 | 150 | try testing.expectEqual(rl, Io.RwLock.init); |
| 151 | |
| 152 | try rl.lock(io); |
| 153 | rl.unlock(io); |
| 154 | try testing.expectEqual(rl, Io.RwLock.init); |
| 114 | 155 | } |
| 115 | 156 | |
| 116 | 157 | test "smoke test" { |
| ... | ... | @@ -123,6 +164,11 @@ test "smoke test" { |
| 123 | 164 | try testing.expect(!rl.tryLockShared(io)); |
| 124 | 165 | rl.unlock(io); |
| 125 | 166 | |
| 167 | try rl.lock(io); |
| 168 | try testing.expect(!rl.tryLock(io)); |
| 169 | try testing.expect(!rl.tryLockShared(io)); |
| 170 | rl.unlock(io); |
| 171 | |
| 126 | 172 | try testing.expect(rl.tryLock(io)); |
| 127 | 173 | try testing.expect(!rl.tryLock(io)); |
| 128 | 174 | try testing.expect(!rl.tryLockShared(io)); |
| ... | ... | @@ -236,3 +282,37 @@ test "concurrent access" { |
| 236 | 282 | try testing.expect(run.writes == num_writes); |
| 237 | 283 | try testing.expect(run.reads.raw >= num_reads); |
| 238 | 284 | } |
| 285 | |
| 286 | test "lock canceling" { |
| 287 | const io = testing.io; |
| 288 | |
| 289 | var rl: Io.RwLock = .init; |
| 290 | |
| 291 | rl.lockSharedUncancelable(io); |
| 292 | var sfuture = io.concurrent(semaphoreLockCancel, .{ &rl, io }) catch |err| switch (err) { |
| 293 | error.ConcurrencyUnavailable => return error.SkipZigTest, |
| 294 | }; |
| 295 | try std.testing.expectEqual(error.Canceled, sfuture.cancel(io)); |
| 296 | rl.unlockShared(io); |
| 297 | try testing.expectEqual(rl, Io.RwLock.init); |
| 298 | |
| 299 | rl.lockUncancelable(io); |
| 300 | var mfuture = io.concurrent(mutexLockCancel, .{ &rl, io }) catch |err| switch (err) { |
| 301 | error.ConcurrencyUnavailable => return error.SkipZigTest, |
| 302 | }; |
| 303 | try std.testing.expectEqual(error.Canceled, mfuture.cancel(io)); |
| 304 | rl.unlock(io); |
| 305 | try testing.expectEqual(rl, Io.RwLock.init); |
| 306 | } |
| 307 | |
| 308 | fn semaphoreLockCancel(rl: *Io.RwLock, io: Io) !void { |
| 309 | try rl.lock(io); //tests semaphore cancelling |
| 310 | } |
| 311 | |
| 312 | fn mutexLockCancel(rl: *Io.RwLock, io: Io) !void { |
| 313 | //tests mutex canceling |
| 314 | try std.testing.expectEqual(error.Canceled, rl.lockShared(io)); |
| 315 | io.recancel(); |
| 316 | try std.testing.expectEqual(error.Canceled, rl.lock(io)); |
| 317 | return error.Canceled; |
| 318 | } |