authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2020-03-10 22:46:19+02:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2020-03-10 22:54:47+02:00
logee5b00a8b90ef375d0cd4432d31e3a4ed0b6f632
treec775663438485092fca336a68a5bcbbc047de818
parent8dc188ebe06b5b78dcead521561858fc27e25204
signaturelock-open Commit is signed but in an unrecognized format.

use atomic bools in std lib


7 files changed, 83 insertions(+), 79 deletions(-)

lib/std/atomic/queue.zig+9-11
...@@ -1,7 +1,5 @@...@@ -1,7 +1,5 @@
1const std = @import("../std.zig");1const std = @import("../std.zig");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const AtomicOrder = builtin.AtomicOrder;
4const AtomicRmwOp = builtin.AtomicRmwOp;
5const assert = std.debug.assert;3const assert = std.debug.assert;
6const expect = std.testing.expect;4const expect = std.testing.expect;
75
...@@ -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 bool150 puts_done: bool,
153};151};
154152
155// TODO add lazy evaluated build options and then put puts_per_thread behind153// 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 };
179177
...@@ -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" {
208206
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();
214212
...@@ -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 fuzz233 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}
248246
249fn startGets(ctx: *Context) u8 {247fn 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);
252250
253 while (ctx.queue.get()) |node| {251 while (ctx.queue.get()) |node| {
254 std.time.sleep(1); // let the os scheduler be our fuzz252 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 }
258256
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 @@
1const assert = std.debug.assert;1const assert = std.debug.assert;
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const AtomicOrder = builtin.AtomicOrder;
4const expect = std.testing.expect;3const expect = std.testing.expect;
54
6/// Many reader, many writer, non-allocating, thread-safe5/// 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),
1312
14 const lock_init = if (builtin.single_threaded) {} else @as(u8, 0);13 const lock_init = if (builtin.single_threaded) {} else false;
1514
16 pub const Self = @This();15 pub const Self = @This();
1716
...@@ -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 }
3635
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);
4443
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);
5857
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 }
6463
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 bool77 puts_done: bool,
79};78};
80// TODO add lazy evaluated build options and then put puts_per_thread behind79// TODO add lazy evaluated build options and then put puts_per_thread behind
81// some option such as: "AggressiveMultithreadedFuzzTest". In the AppVeyor80// 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 };
104103
...@@ -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" {
128127
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}
165164
166fn startGets(ctx: *Context) u8 {165fn 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;
169168
170 while (ctx.stack.pop()) |node| {169 while (ctx.stack.pop()) |node| {
171 std.time.sleep(1); // let the os scheduler be our fuzz170 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 }
175174
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 bool17 dispatch_lock: bool,
18 need_dispatch: u8, // TODO make this a bool18 need_dispatch: bool,
1919
20 // simple fixed size ring buffer20 // 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 {
165165
166 fn dispatch(self: *SelfChannel) void {166 fn dispatch(self: *SelfChannel) void {
167 // set the "need dispatch" flag167 // set the "need dispatch" flag
168 @atomicStore(u8, &self.need_dispatch, 1, .SeqCst);168 @atomicStore(bool, &self.need_dispatch, true, .SeqCst);
169169
170 lock: while (true) {170 lock: while (true) {
171 // set the lock flag171 // 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;
174174
175 // clear the need_dispatch flag since we're about to do it175 // 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);
177177
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 }
251251
252 // clear need-dispatch flag252 // 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;
255255
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);
258258
259 // we have to check again now that we unlocked259 // 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;
261261
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.
13pub const Lock = struct {13pub const Lock = struct {
14 shared_bit: u8, // TODO make this a bool14 shared: bool,
15 queue: Queue,15 queue: Queue,
16 queue_empty_bit: u8, // TODO make this a bool16 queue_empty: bool,
1717
18 const Queue = std.atomic.Queue(anyframe);18 const Queue = std.atomic.Queue(anyframe);
1919
...@@ -31,20 +31,19 @@ pub const Lock = struct {...@@ -31,20 +31,19 @@ pub const Lock = struct {
31 }31 }
3232
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);
3636
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 attempt39 // 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 }
4444
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 }
5756
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);
6160
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 {
6968
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 }
7776
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 }
8584
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 }
9291
...@@ -99,12 +98,11 @@ pub const Lock = struct {...@@ -99,12 +98,11 @@ pub const Lock = struct {
9998
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.
101100
102 // We set this bit so that later we can rely on the fact, that if queue_empty_bit is 1, some actor101 // 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);
105104
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 bool19 writer_queue_empty: bool,
20 reader_queue_empty_bit: u8, // TODO make this a bool20 reader_queue_empty: bool,
21 reader_lock_count: usize,21 reader_lock_count: usize,
2222
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 }
4242
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 }
6363
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 }
7373
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);
7676
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 };
8080
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 {
111111
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.
113113
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);
117117
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 {
142142
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.
144144
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);
148148
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 }
183183
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
2839728397
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 }
2841128411
test/compile_errors.zig+11-2
...@@ -2,6 +2,15 @@ const tests = @import("tests.zig");...@@ -2,6 +2,15 @@ const tests = @import("tests.zig");
2const std = @import("std");2const std = @import("std");
33
4pub fn addCases(cases: *tests.CompileErrorContext) void {4pub 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 });
330339
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 });
547556
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",