| author | |
| committer | |
| log | ee5b00a8b90ef375d0cd4432d31e3a4ed0b6f632 |
| tree | c775663438485092fca336a68a5bcbbc047de818 |
| parent | 8dc188ebe06b5b78dcead521561858fc27e25204 |
| signature |
7 files changed, 83 insertions(+), 79 deletions(-)
lib/std/atomic/queue.zig+9-11| ... | ... | @@ -1,7 +1,5 @@ |
| 1 | 1 | const std = @import("../std.zig"); |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | const AtomicOrder = builtin.AtomicOrder; | |
| 4 | const AtomicRmwOp = builtin.AtomicRmwOp; | |
| 5 | 3 | const assert = std.debug.assert; |
| 6 | 4 | const expect = std.testing.expect; |
| 7 | 5 | |
| ... | ... | @@ -149,7 +147,7 @@ const Context = struct { |
| 149 | 147 | put_sum: isize, |
| 150 | 148 | get_sum: isize, |
| 151 | 149 | get_count: usize, |
| 152 | puts_done: u8, // TODO make this a bool | |
| 150 | puts_done: bool, | |
| 153 | 151 | }; |
| 154 | 152 | |
| 155 | 153 | // TODO add lazy evaluated build options and then put puts_per_thread behind |
| ... | ... | @@ -173,7 +171,7 @@ test "std.atomic.Queue" { |
| 173 | 171 | .queue = &queue, |
| 174 | 172 | .put_sum = 0, |
| 175 | 173 | .get_sum = 0, |
| 176 | .puts_done = 0, | |
| 174 | .puts_done = false, | |
| 177 | 175 | .get_count = 0, |
| 178 | 176 | }; |
| 179 | 177 | |
| ... | ... | @@ -186,7 +184,7 @@ test "std.atomic.Queue" { |
| 186 | 184 | } |
| 187 | 185 | } |
| 188 | 186 | expect(!context.queue.isEmpty()); |
| 189 | context.puts_done = 1; | |
| 187 | context.puts_done = true; | |
| 190 | 188 | { |
| 191 | 189 | var i: usize = 0; |
| 192 | 190 | while (i < put_thread_count) : (i += 1) { |
| ... | ... | @@ -208,7 +206,7 @@ test "std.atomic.Queue" { |
| 208 | 206 | |
| 209 | 207 | for (putters) |t| |
| 210 | 208 | t.wait(); |
| 211 | @atomicStore(u8, &context.puts_done, 1, AtomicOrder.SeqCst); | |
| 209 | @atomicStore(bool, &context.puts_done, true, .SeqCst); | |
| 212 | 210 | for (getters) |t| |
| 213 | 211 | t.wait(); |
| 214 | 212 | |
| ... | ... | @@ -235,25 +233,25 @@ fn startPuts(ctx: *Context) u8 { |
| 235 | 233 | std.time.sleep(1); // let the os scheduler be our fuzz |
| 236 | 234 | const x = @bitCast(i32, r.random.scalar(u32)); |
| 237 | 235 | const node = ctx.allocator.create(Queue(i32).Node) catch unreachable; |
| 238 | node.* = Queue(i32).Node{ | |
| 236 | node.* = .{ | |
| 239 | 237 | .prev = undefined, |
| 240 | 238 | .next = undefined, |
| 241 | 239 | .data = x, |
| 242 | 240 | }; |
| 243 | 241 | ctx.queue.put(node); |
| 244 | _ = @atomicRmw(isize, &ctx.put_sum, builtin.AtomicRmwOp.Add, x, AtomicOrder.SeqCst); | |
| 242 | _ = @atomicRmw(isize, &ctx.put_sum, .Add, x, .SeqCst); | |
| 245 | 243 | } |
| 246 | 244 | return 0; |
| 247 | 245 | } |
| 248 | 246 | |
| 249 | 247 | fn startGets(ctx: *Context) u8 { |
| 250 | 248 | while (true) { |
| 251 | const last = @atomicLoad(u8, &ctx.puts_done, builtin.AtomicOrder.SeqCst) == 1; | |
| 249 | const last = @atomicLoad(bool, &ctx.puts_done, .SeqCst); | |
| 252 | 250 | |
| 253 | 251 | while (ctx.queue.get()) |node| { |
| 254 | 252 | std.time.sleep(1); // let the os scheduler be our fuzz |
| 255 | _ = @atomicRmw(isize, &ctx.get_sum, builtin.AtomicRmwOp.Add, node.data, builtin.AtomicOrder.SeqCst); | |
| 256 | _ = @atomicRmw(usize, &ctx.get_count, builtin.AtomicRmwOp.Add, 1, builtin.AtomicOrder.SeqCst); | |
| 253 | _ = @atomicRmw(isize, &ctx.get_sum, .Add, node.data, .SeqCst); | |
| 254 | _ = @atomicRmw(usize, &ctx.get_count, .Add, 1, .SeqCst); | |
| 257 | 255 | } |
| 258 | 256 | |
| 259 | 257 | if (last) return 0; |
lib/std/atomic/stack.zig+15-16| ... | ... | @@ -1,6 +1,5 @@ |
| 1 | 1 | const assert = std.debug.assert; |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | const AtomicOrder = builtin.AtomicOrder; | |
| 4 | 3 | const expect = std.testing.expect; |
| 5 | 4 | |
| 6 | 5 | /// Many reader, many writer, non-allocating, thread-safe |
| ... | ... | @@ -11,7 +10,7 @@ pub fn Stack(comptime T: type) type { |
| 11 | 10 | root: ?*Node, |
| 12 | 11 | lock: @TypeOf(lock_init), |
| 13 | 12 | |
| 14 | const lock_init = if (builtin.single_threaded) {} else @as(u8, 0); | |
| 13 | const lock_init = if (builtin.single_threaded) {} else false; | |
| 15 | 14 | |
| 16 | 15 | pub const Self = @This(); |
| 17 | 16 | |
| ... | ... | @@ -31,7 +30,7 @@ pub fn Stack(comptime T: type) type { |
| 31 | 30 | /// being the first item in the stack, returns the other item that was there. |
| 32 | 31 | pub fn pushFirst(self: *Self, node: *Node) ?*Node { |
| 33 | 32 | node.next = null; |
| 34 | return @cmpxchgStrong(?*Node, &self.root, null, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst); | |
| 33 | return @cmpxchgStrong(?*Node, &self.root, null, node, .SeqCst, .SeqCst); | |
| 35 | 34 | } |
| 36 | 35 | |
| 37 | 36 | pub fn push(self: *Self, node: *Node) void { |
| ... | ... | @@ -39,8 +38,8 @@ pub fn Stack(comptime T: type) type { |
| 39 | 38 | node.next = self.root; |
| 40 | 39 | self.root = node; |
| 41 | 40 | } else { |
| 42 | while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {} | |
| 43 | defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1); | |
| 41 | while (@atomicRmw(bool, &self.lock, .Xchg, true, .SeqCst) != false) {} | |
| 42 | defer assert(@atomicRmw(bool, &self.lock, .Xchg, false, .SeqCst) == true); | |
| 44 | 43 | |
| 45 | 44 | node.next = self.root; |
| 46 | 45 | self.root = node; |
| ... | ... | @@ -53,8 +52,8 @@ pub fn Stack(comptime T: type) type { |
| 53 | 52 | self.root = root.next; |
| 54 | 53 | return root; |
| 55 | 54 | } else { |
| 56 | while (@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) != 0) {} | |
| 57 | defer assert(@atomicRmw(u8, &self.lock, builtin.AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst) == 1); | |
| 55 | while (@atomicRmw(bool, &self.lock, .Xchg, true, .SeqCst) != false) {} | |
| 56 | defer assert(@atomicRmw(bool, &self.lock, .Xchg, false, .SeqCst) == true); | |
| 58 | 57 | |
| 59 | 58 | const root = self.root orelse return null; |
| 60 | 59 | self.root = root.next; |
| ... | ... | @@ -63,7 +62,7 @@ pub fn Stack(comptime T: type) type { |
| 63 | 62 | } |
| 64 | 63 | |
| 65 | 64 | pub fn isEmpty(self: *Self) bool { |
| 66 | return @atomicLoad(?*Node, &self.root, AtomicOrder.SeqCst) == null; | |
| 65 | return @atomicLoad(?*Node, &self.root, .SeqCst) == null; | |
| 67 | 66 | } |
| 68 | 67 | }; |
| 69 | 68 | } |
| ... | ... | @@ -75,7 +74,7 @@ const Context = struct { |
| 75 | 74 | put_sum: isize, |
| 76 | 75 | get_sum: isize, |
| 77 | 76 | get_count: usize, |
| 78 | puts_done: u8, // TODO make this a bool | |
| 77 | puts_done: bool, | |
| 79 | 78 | }; |
| 80 | 79 | // TODO add lazy evaluated build options and then put puts_per_thread behind |
| 81 | 80 | // some option such as: "AggressiveMultithreadedFuzzTest". In the AppVeyor |
| ... | ... | @@ -98,7 +97,7 @@ test "std.atomic.stack" { |
| 98 | 97 | .stack = &stack, |
| 99 | 98 | .put_sum = 0, |
| 100 | 99 | .get_sum = 0, |
| 101 | .puts_done = 0, | |
| 100 | .puts_done = false, | |
| 102 | 101 | .get_count = 0, |
| 103 | 102 | }; |
| 104 | 103 | |
| ... | ... | @@ -109,7 +108,7 @@ test "std.atomic.stack" { |
| 109 | 108 | expect(startPuts(&context) == 0); |
| 110 | 109 | } |
| 111 | 110 | } |
| 112 | context.puts_done = 1; | |
| 111 | context.puts_done = true; | |
| 113 | 112 | { |
| 114 | 113 | var i: usize = 0; |
| 115 | 114 | while (i < put_thread_count) : (i += 1) { |
| ... | ... | @@ -128,7 +127,7 @@ test "std.atomic.stack" { |
| 128 | 127 | |
| 129 | 128 | for (putters) |t| |
| 130 | 129 | t.wait(); |
| 131 | @atomicStore(u8, &context.puts_done, 1, AtomicOrder.SeqCst); | |
| 130 | @atomicStore(bool, &context.puts_done, true, .SeqCst); | |
| 132 | 131 | for (getters) |t| |
| 133 | 132 | t.wait(); |
| 134 | 133 | } |
| ... | ... | @@ -158,19 +157,19 @@ fn startPuts(ctx: *Context) u8 { |
| 158 | 157 | .data = x, |
| 159 | 158 | }; |
| 160 | 159 | ctx.stack.push(node); |
| 161 | _ = @atomicRmw(isize, &ctx.put_sum, builtin.AtomicRmwOp.Add, x, AtomicOrder.SeqCst); | |
| 160 | _ = @atomicRmw(isize, &ctx.put_sum, .Add, x, .SeqCst); | |
| 162 | 161 | } |
| 163 | 162 | return 0; |
| 164 | 163 | } |
| 165 | 164 | |
| 166 | 165 | fn startGets(ctx: *Context) u8 { |
| 167 | 166 | while (true) { |
| 168 | const last = @atomicLoad(u8, &ctx.puts_done, builtin.AtomicOrder.SeqCst) == 1; | |
| 167 | const last = @atomicLoad(bool, &ctx.puts_done, .SeqCst) == true; | |
| 169 | 168 | |
| 170 | 169 | while (ctx.stack.pop()) |node| { |
| 171 | 170 | std.time.sleep(1); // let the os scheduler be our fuzz |
| 172 | _ = @atomicRmw(isize, &ctx.get_sum, builtin.AtomicRmwOp.Add, node.data, builtin.AtomicOrder.SeqCst); | |
| 173 | _ = @atomicRmw(usize, &ctx.get_count, builtin.AtomicRmwOp.Add, 1, builtin.AtomicOrder.SeqCst); | |
| 171 | _ = @atomicRmw(isize, &ctx.get_sum, .Add, node.data, .SeqCst); | |
| 172 | _ = @atomicRmw(usize, &ctx.get_count, .Add, 1, .SeqCst); | |
| 174 | 173 | } |
| 175 | 174 | |
| 176 | 175 | if (last) return 0; |
lib/std/event/channel.zig+12-12| ... | ... | @@ -14,8 +14,8 @@ pub fn Channel(comptime T: type) type { |
| 14 | 14 | putters: std.atomic.Queue(PutNode), |
| 15 | 15 | get_count: usize, |
| 16 | 16 | put_count: usize, |
| 17 | dispatch_lock: u8, // TODO make this a bool | |
| 18 | need_dispatch: u8, // TODO make this a bool | |
| 17 | dispatch_lock: bool, | |
| 18 | need_dispatch: bool, | |
| 19 | 19 | |
| 20 | 20 | // simple fixed size ring buffer |
| 21 | 21 | buffer_nodes: []T, |
| ... | ... | @@ -62,8 +62,8 @@ pub fn Channel(comptime T: type) type { |
| 62 | 62 | .buffer_len = 0, |
| 63 | 63 | .buffer_nodes = buffer, |
| 64 | 64 | .buffer_index = 0, |
| 65 | .dispatch_lock = 0, | |
| 66 | .need_dispatch = 0, | |
| 65 | .dispatch_lock = false, | |
| 66 | .need_dispatch = false, | |
| 67 | 67 | .getters = std.atomic.Queue(GetNode).init(), |
| 68 | 68 | .putters = std.atomic.Queue(PutNode).init(), |
| 69 | 69 | .or_null_queue = std.atomic.Queue(*std.atomic.Queue(GetNode).Node).init(), |
| ... | ... | @@ -165,15 +165,15 @@ pub fn Channel(comptime T: type) type { |
| 165 | 165 | |
| 166 | 166 | fn dispatch(self: *SelfChannel) void { |
| 167 | 167 | // set the "need dispatch" flag |
| 168 | @atomicStore(u8, &self.need_dispatch, 1, .SeqCst); | |
| 168 | @atomicStore(bool, &self.need_dispatch, true, .SeqCst); | |
| 169 | 169 | |
| 170 | 170 | lock: while (true) { |
| 171 | 171 | // set the lock flag |
| 172 | const prev_lock = @atomicRmw(u8, &self.dispatch_lock, .Xchg, 1, .SeqCst); | |
| 172 | const prev_lock = @atomicRmw(bool, &self.dispatch_lock, .Xchg, true, .SeqCst); | |
| 173 | 173 | if (prev_lock != 0) return; |
| 174 | 174 | |
| 175 | 175 | // clear the need_dispatch flag since we're about to do it |
| 176 | @atomicStore(u8, &self.need_dispatch, 0, .SeqCst); | |
| 176 | @atomicStore(bool, &self.need_dispatch, false, .SeqCst); | |
| 177 | 177 | |
| 178 | 178 | while (true) { |
| 179 | 179 | one_dispatch: { |
| ... | ... | @@ -250,14 +250,14 @@ pub fn Channel(comptime T: type) type { |
| 250 | 250 | } |
| 251 | 251 | |
| 252 | 252 | // clear need-dispatch flag |
| 253 | const need_dispatch = @atomicRmw(u8, &self.need_dispatch, .Xchg, 0, .SeqCst); | |
| 254 | if (need_dispatch != 0) continue; | |
| 253 | const need_dispatch = @atomicRmw(bool, &self.need_dispatch, .Xchg, false, .SeqCst); | |
| 254 | if (need_dispatch) continue; | |
| 255 | 255 | |
| 256 | const my_lock = @atomicRmw(u8, &self.dispatch_lock, .Xchg, 0, .SeqCst); | |
| 257 | assert(my_lock != 0); | |
| 256 | const my_lock = @atomicRmw(bool, &self.dispatch_lock, .Xchg, false, .SeqCst); | |
| 257 | assert(my_lock); | |
| 258 | 258 | |
| 259 | 259 | // we have to check again now that we unlocked |
| 260 | if (@atomicLoad(u8, &self.need_dispatch, .SeqCst) != 0) continue :lock; | |
| 260 | if (@atomicLoad(bool, &self.need_dispatch, .SeqCst)) continue :lock; | |
| 261 | 261 | |
| 262 | 262 | return; |
| 263 | 263 | } |
lib/std/event/lock.zig+17-19| ... | ... | @@ -11,9 +11,9 @@ const Loop = std.event.Loop; |
| 11 | 11 | /// Allows only one actor to hold the lock. |
| 12 | 12 | /// TODO: make this API also work in blocking I/O mode. |
| 13 | 13 | pub const Lock = struct { |
| 14 | shared_bit: u8, // TODO make this a bool | |
| 14 | shared: bool, | |
| 15 | 15 | queue: Queue, |
| 16 | queue_empty_bit: u8, // TODO make this a bool | |
| 16 | queue_empty: bool, | |
| 17 | 17 | |
| 18 | 18 | const Queue = std.atomic.Queue(anyframe); |
| 19 | 19 | |
| ... | ... | @@ -31,20 +31,19 @@ pub const Lock = struct { |
| 31 | 31 | } |
| 32 | 32 | |
| 33 | 33 | // We need to release the lock. |
| 34 | @atomicStore(u8, &self.lock.queue_empty_bit, 1, .SeqCst); | |
| 35 | @atomicStore(u8, &self.lock.shared_bit, 0, .SeqCst); | |
| 34 | @atomicStore(bool, &self.lock.queue_empty, true, .SeqCst); | |
| 35 | @atomicStore(bool, &self.lock.shared, false, .SeqCst); | |
| 36 | 36 | |
| 37 | 37 | // There might be a queue item. If we know the queue is empty, we can be done, |
| 38 | 38 | // because the other actor will try to obtain the lock. |
| 39 | 39 | // But if there's a queue item, we are the actor which must loop and attempt |
| 40 | 40 | // to grab the lock again. |
| 41 | if (@atomicLoad(u8, &self.lock.queue_empty_bit, .SeqCst) == 1) { | |
| 41 | if (@atomicLoad(bool, &self.lock.queue_empty, .SeqCst)) { | |
| 42 | 42 | return; |
| 43 | 43 | } |
| 44 | 44 | |
| 45 | 45 | while (true) { |
| 46 | const old_bit = @atomicRmw(u8, &self.lock.shared_bit, .Xchg, 1, .SeqCst); | |
| 47 | if (old_bit != 0) { | |
| 46 | if (@atomicRmw(bool, &self.lock.shared, .Xchg, true, .SeqCst)) { | |
| 48 | 47 | // We did not obtain the lock. Great, the queue is someone else's problem. |
| 49 | 48 | return; |
| 50 | 49 | } |
| ... | ... | @@ -56,11 +55,11 @@ pub const Lock = struct { |
| 56 | 55 | } |
| 57 | 56 | |
| 58 | 57 | // Release the lock again. |
| 59 | @atomicStore(u8, &self.lock.queue_empty_bit, 1, .SeqCst); | |
| 60 | @atomicStore(u8, &self.lock.shared_bit, 0, .SeqCst); | |
| 58 | @atomicStore(bool, &self.lock.queue_empty, true, .SeqCst); | |
| 59 | @atomicStore(bool, &self.lock.shared, false, .SeqCst); | |
| 61 | 60 | |
| 62 | 61 | // Find out if we can be done. |
| 63 | if (@atomicLoad(u8, &self.lock.queue_empty_bit, .SeqCst) == 1) { | |
| 62 | if (@atomicLoad(bool, &self.lock.queue_empty, .SeqCst)) { | |
| 64 | 63 | return; |
| 65 | 64 | } |
| 66 | 65 | } |
| ... | ... | @@ -69,24 +68,24 @@ pub const Lock = struct { |
| 69 | 68 | |
| 70 | 69 | pub fn init() Lock { |
| 71 | 70 | return Lock{ |
| 72 | .shared_bit = 0, | |
| 71 | .shared = false, | |
| 73 | 72 | .queue = Queue.init(), |
| 74 | .queue_empty_bit = 1, | |
| 73 | .queue_empty = true, | |
| 75 | 74 | }; |
| 76 | 75 | } |
| 77 | 76 | |
| 78 | 77 | pub fn initLocked() Lock { |
| 79 | 78 | return Lock{ |
| 80 | .shared_bit = 1, | |
| 79 | .shared = true, | |
| 81 | 80 | .queue = Queue.init(), |
| 82 | .queue_empty_bit = 1, | |
| 81 | .queue_empty = true, | |
| 83 | 82 | }; |
| 84 | 83 | } |
| 85 | 84 | |
| 86 | 85 | /// Must be called when not locked. Not thread safe. |
| 87 | 86 | /// All calls to acquire() and release() must complete before calling deinit(). |
| 88 | 87 | pub fn deinit(self: *Lock) void { |
| 89 | assert(self.shared_bit == 0); | |
| 88 | assert(!self.shared); | |
| 90 | 89 | while (self.queue.get()) |node| resume node.data; |
| 91 | 90 | } |
| 92 | 91 | |
| ... | ... | @@ -99,12 +98,11 @@ pub const Lock = struct { |
| 99 | 98 | |
| 100 | 99 | // At this point, we are in the queue, so we might have already been resumed. |
| 101 | 100 | |
| 102 | // We set this bit so that later we can rely on the fact, that if queue_empty_bit is 1, some actor | |
| 101 | // We set this bit so that later we can rely on the fact, that if queue_empty == true, some actor | |
| 103 | 102 | // will attempt to grab the lock. |
| 104 | @atomicStore(u8, &self.queue_empty_bit, 0, .SeqCst); | |
| 103 | @atomicStore(bool, &self.queue_empty, false, .SeqCst); | |
| 105 | 104 | |
| 106 | const old_bit = @atomicRmw(u8, &self.shared_bit, .Xchg, 1, .SeqCst); | |
| 107 | if (old_bit == 0) { | |
| 105 | if (!@atomicRmw(bool, &self.shared, .Xchg, true, .SeqCst)) { | |
| 108 | 106 | if (self.queue.get()) |node| { |
| 109 | 107 | // Whether this node is us or someone else, we tail resume it. |
| 110 | 108 | resume node.data; |
lib/std/event/rwlock.zig+16-16| ... | ... | @@ -16,8 +16,8 @@ pub const RwLock = struct { |
| 16 | 16 | shared_state: State, |
| 17 | 17 | writer_queue: Queue, |
| 18 | 18 | reader_queue: Queue, |
| 19 | writer_queue_empty_bit: u8, // TODO make this a bool | |
| 20 | reader_queue_empty_bit: u8, // TODO make this a bool | |
| 19 | writer_queue_empty: bool, | |
| 20 | reader_queue_empty: bool, | |
| 21 | 21 | reader_lock_count: usize, |
| 22 | 22 | |
| 23 | 23 | const State = enum(u8) { |
| ... | ... | @@ -40,7 +40,7 @@ pub const RwLock = struct { |
| 40 | 40 | return; |
| 41 | 41 | } |
| 42 | 42 | |
| 43 | @atomicStore(u8, &self.lock.reader_queue_empty_bit, 1, .SeqCst); | |
| 43 | @atomicStore(bool, &self.lock.reader_queue_empty, true, .SeqCst); | |
| 44 | 44 | if (@cmpxchgStrong(State, &self.lock.shared_state, .ReadLock, .Unlocked, .SeqCst, .SeqCst) != null) { |
| 45 | 45 | // Didn't unlock. Someone else's problem. |
| 46 | 46 | return; |
| ... | ... | @@ -62,7 +62,7 @@ pub const RwLock = struct { |
| 62 | 62 | } |
| 63 | 63 | |
| 64 | 64 | // We need to release the write lock. Check if any readers are waiting to grab the lock. |
| 65 | if (@atomicLoad(u8, &self.lock.reader_queue_empty_bit, .SeqCst) == 0) { | |
| 65 | if (!@atomicLoad(bool, &self.lock.reader_queue_empty, .SeqCst)) { | |
| 66 | 66 | // Switch to a read lock. |
| 67 | 67 | @atomicStore(State, &self.lock.shared_state, .ReadLock, .SeqCst); |
| 68 | 68 | while (self.lock.reader_queue.get()) |node| { |
| ... | ... | @@ -71,7 +71,7 @@ pub const RwLock = struct { |
| 71 | 71 | return; |
| 72 | 72 | } |
| 73 | 73 | |
| 74 | @atomicStore(u8, &self.lock.writer_queue_empty_bit, 1, .SeqCst); | |
| 74 | @atomicStore(bool, &self.lock.writer_queue_empty, true, .SeqCst); | |
| 75 | 75 | @atomicStore(State, &self.lock.shared_state, .Unlocked, .SeqCst); |
| 76 | 76 | |
| 77 | 77 | self.lock.commonPostUnlock(); |
| ... | ... | @@ -79,12 +79,12 @@ pub const RwLock = struct { |
| 79 | 79 | }; |
| 80 | 80 | |
| 81 | 81 | pub fn init() RwLock { |
| 82 | return RwLock{ | |
| 82 | return .{ | |
| 83 | 83 | .shared_state = .Unlocked, |
| 84 | 84 | .writer_queue = Queue.init(), |
| 85 | .writer_queue_empty_bit = 1, | |
| 85 | .writer_queue_empty = true, | |
| 86 | 86 | .reader_queue = Queue.init(), |
| 87 | .reader_queue_empty_bit = 1, | |
| 87 | .reader_queue_empty = true, | |
| 88 | 88 | .reader_lock_count = 0, |
| 89 | 89 | }; |
| 90 | 90 | } |
| ... | ... | @@ -111,9 +111,9 @@ pub const RwLock = struct { |
| 111 | 111 | |
| 112 | 112 | // At this point, we are in the reader_queue, so we might have already been resumed. |
| 113 | 113 | |
| 114 | // We set this bit so that later we can rely on the fact, that if reader_queue_empty_bit is 1, | |
| 114 | // We set this bit so that later we can rely on the fact, that if reader_queue_empty == true, | |
| 115 | 115 | // some actor will attempt to grab the lock. |
| 116 | @atomicStore(u8, &self.reader_queue_empty_bit, 0, .SeqCst); | |
| 116 | @atomicStore(bool, &self.reader_queue_empty, false, .SeqCst); | |
| 117 | 117 | |
| 118 | 118 | // Here we don't care if we are the one to do the locking or if it was already locked for reading. |
| 119 | 119 | const have_read_lock = if (@cmpxchgStrong(State, &self.shared_state, .Unlocked, .ReadLock, .SeqCst, .SeqCst)) |old_state| old_state == .ReadLock else true; |
| ... | ... | @@ -142,9 +142,9 @@ pub const RwLock = struct { |
| 142 | 142 | |
| 143 | 143 | // At this point, we are in the writer_queue, so we might have already been resumed. |
| 144 | 144 | |
| 145 | // We set this bit so that later we can rely on the fact, that if writer_queue_empty_bit is 1, | |
| 145 | // We set this bit so that later we can rely on the fact, that if writer_queue_empty == true, | |
| 146 | 146 | // some actor will attempt to grab the lock. |
| 147 | @atomicStore(u8, &self.writer_queue_empty_bit, 0, .SeqCst); | |
| 147 | @atomicStore(bool, &self.writer_queue_empty, false, .SeqCst); | |
| 148 | 148 | |
| 149 | 149 | // Here we must be the one to acquire the write lock. It cannot already be locked. |
| 150 | 150 | if (@cmpxchgStrong(State, &self.shared_state, .Unlocked, .WriteLock, .SeqCst, .SeqCst) == null) { |
| ... | ... | @@ -165,7 +165,7 @@ pub const RwLock = struct { |
| 165 | 165 | // obtain the lock. |
| 166 | 166 | // But if there's a writer_queue item or a reader_queue item, |
| 167 | 167 | // we are the actor which must loop and attempt to grab the lock again. |
| 168 | if (@atomicLoad(u8, &self.writer_queue_empty_bit, .SeqCst) == 0) { | |
| 168 | if (!@atomicLoad(bool, &self.writer_queue_empty, .SeqCst)) { | |
| 169 | 169 | if (@cmpxchgStrong(State, &self.shared_state, .Unlocked, .WriteLock, .SeqCst, .SeqCst) != null) { |
| 170 | 170 | // We did not obtain the lock. Great, the queues are someone else's problem. |
| 171 | 171 | return; |
| ... | ... | @@ -176,12 +176,12 @@ pub const RwLock = struct { |
| 176 | 176 | return; |
| 177 | 177 | } |
| 178 | 178 | // Release the lock again. |
| 179 | @atomicStore(u8, &self.writer_queue_empty_bit, 1, .SeqCst); | |
| 179 | @atomicStore(bool, &self.writer_queue_empty, true, .SeqCst); | |
| 180 | 180 | @atomicStore(State, &self.shared_state, .Unlocked, .SeqCst); |
| 181 | 181 | continue; |
| 182 | 182 | } |
| 183 | 183 | |
| 184 | if (@atomicLoad(u8, &self.reader_queue_empty_bit, .SeqCst) == 0) { | |
| 184 | if (!@atomicLoad(bool, &self.reader_queue_empty, .SeqCst)) { | |
| 185 | 185 | if (@cmpxchgStrong(State, &self.shared_state, .Unlocked, .ReadLock, .SeqCst, .SeqCst) != null) { |
| 186 | 186 | // We did not obtain the lock. Great, the queues are someone else's problem. |
| 187 | 187 | return; |
| ... | ... | @@ -195,7 +195,7 @@ pub const RwLock = struct { |
| 195 | 195 | return; |
| 196 | 196 | } |
| 197 | 197 | // Release the lock again. |
| 198 | @atomicStore(u8, &self.reader_queue_empty_bit, 1, .SeqCst); | |
| 198 | @atomicStore(bool, &self.reader_queue_empty, true, .SeqCst); | |
| 199 | 199 | if (@cmpxchgStrong(State, &self.shared_state, .ReadLock, .Unlocked, .SeqCst, .SeqCst) != null) { |
| 200 | 200 | // Didn't unlock. Someone else's problem. |
| 201 | 201 | return; |
src/ir.cpp+3-3| ... | ... | @@ -28397,15 +28397,15 @@ static IrInstGen *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstSrcAto |
| 28397 | 28397 | |
| 28398 | 28398 | if (operand_type->id == ZigTypeIdEnum && op != AtomicRmwOp_xchg) { |
| 28399 | 28399 | ir_add_error(ira, &instruction->op->base, |
| 28400 | buf_sprintf("@atomicRmw on enum only works with .Xchg")); | |
| 28400 | buf_sprintf("@atomicRmw with enum only allowed with .Xchg")); | |
| 28401 | 28401 | return ira->codegen->invalid_inst_gen; |
| 28402 | 28402 | } else if (operand_type->id == ZigTypeIdBool && op != AtomicRmwOp_xchg) { |
| 28403 | 28403 | ir_add_error(ira, &instruction->op->base, |
| 28404 | buf_sprintf("@atomicRmw on bool only works with .Xchg")); | |
| 28404 | buf_sprintf("@atomicRmw with bool only allowed with .Xchg")); | |
| 28405 | 28405 | return ira->codegen->invalid_inst_gen; |
| 28406 | 28406 | } else if (operand_type->id == ZigTypeIdFloat && op > AtomicRmwOp_sub) { |
| 28407 | 28407 | ir_add_error(ira, &instruction->op->base, |
| 28408 | buf_sprintf("@atomicRmw with float only works with .Xchg, .Add and .Sub")); | |
| 28408 | buf_sprintf("@atomicRmw with float only allowed with .Xchg, .Add and .Sub")); | |
| 28409 | 28409 | return ira->codegen->invalid_inst_gen; |
| 28410 | 28410 | } |
| 28411 | 28411 |
test/compile_errors.zig+11-2| ... | ... | @@ -2,6 +2,15 @@ const tests = @import("tests.zig"); |
| 2 | 2 | const std = @import("std"); |
| 3 | 3 | |
| 4 | 4 | pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5 | cases.add("atomicrmw with bool op not .Xchg", | |
| 6 | \\export fn entry() void { | |
| 7 | \\ var x = false; | |
| 8 | \\ _ = @atomicRmw(bool, &x, .Add, true, .SeqCst); | |
| 9 | \\} | |
| 10 | , &[_][]const u8{ | |
| 11 | "tmp.zig:3:30: error: @atomicRmw with bool only allowed with .Xchg", | |
| 12 | }); | |
| 13 | ||
| 5 | 14 | cases.addTest("combination of noasync and async", |
| 6 | 15 | \\export fn entry() void { |
| 7 | 16 | \\ noasync { |
| ... | ... | @@ -325,7 +334,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 325 | 334 | \\ _ = @atomicRmw(f32, &x, .And, 2, .SeqCst); |
| 326 | 335 | \\} |
| 327 | 336 | , &[_][]const u8{ |
| 328 | "tmp.zig:3:29: error: @atomicRmw with float only works with .Xchg, .Add and .Sub", | |
| 337 | "tmp.zig:3:29: error: @atomicRmw with float only allowed with .Xchg, .Add and .Sub", | |
| 329 | 338 | }); |
| 330 | 339 | |
| 331 | 340 | cases.add("intToPtr with misaligned address", |
| ... | ... | @@ -542,7 +551,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 542 | 551 | \\ _ = @atomicRmw(E, &x, .Add, .b, .SeqCst); |
| 543 | 552 | \\} |
| 544 | 553 | , &[_][]const u8{ |
| 545 | "tmp.zig:9:27: error: @atomicRmw on enum only works with .Xchg", | |
| 554 | "tmp.zig:9:27: error: @atomicRmw with enum only allowed with .Xchg", | |
| 546 | 555 | }); |
| 547 | 556 | |
| 548 | 557 | cases.add("disallow coercion from non-null-terminated pointer to null-terminated pointer", |