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