authorgravatar for qbradley@qbradley.comQuetzal Bradley <qbradley@qbradley.com> 2019-11-28 02:19:08+00:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-11-27 23:09:24-05:00
loga6c9c5f767217ba3591e51047736f0fa6a975a57
treef2bd2ffa851078b492c3ec4cd762e1957753b755
parentca61a5f0b74fb420a21cdbb9e58f9d38ebe9dc0f

implement correct buffer wrapping logic in std.event.Channel


1 files changed, 30 insertions(+), 3 deletions(-)

lib/std/event/channel.zig+30-3
......@@ -54,6 +54,10 @@ pub fn Channel(comptime T: type) type {
5454 /// For a zero length buffer, use `[0]T{}`.
5555 /// TODO https://github.com/ziglang/zig/issues/2765
5656 pub fn init(self: *SelfChannel, buffer: []T) void {
57 // The ring buffer implementation only works with power of 2 buffer sizes
58 // because of relying on subtracting across zero. For example (0 -% 1) % 10 == 5
59 assert(buffer.len == 0 or @popCount(usize, buffer.len) == 1);
60
5761 self.* = SelfChannel{
5862 .buffer_len = 0,
5963 .buffer_nodes = buffer,
......@@ -184,11 +188,11 @@ pub fn Channel(comptime T: type) type {
184188 const get_node = &self.getters.get().?.data;
185189 switch (get_node.data) {
186190 GetNode.Data.Normal => |info| {
187 info.ptr.* = self.buffer_nodes[self.buffer_index -% self.buffer_len];
191 info.ptr.* = self.buffer_nodes[(self.buffer_index -% self.buffer_len) % self.buffer_nodes.len];
188192 },
189193 GetNode.Data.OrNull => |info| {
190194 _ = self.or_null_queue.remove(info.or_null);
191 info.ptr.* = self.buffer_nodes[self.buffer_index -% self.buffer_len];
195 info.ptr.* = self.buffer_nodes[(self.buffer_index -% self.buffer_len) % self.buffer_nodes.len];
192196 },
193197 }
194198 global_event_loop.onNextTick(get_node.tick_node);
......@@ -222,7 +226,7 @@ pub fn Channel(comptime T: type) type {
222226 while (self.buffer_len != self.buffer_nodes.len and put_count != 0) {
223227 const put_node = &self.putters.get().?.data;
224228
225 self.buffer_nodes[self.buffer_index] = put_node.data;
229 self.buffer_nodes[self.buffer_index % self.buffer_nodes.len] = put_node.data;
226230 global_event_loop.onNextTick(put_node.tick_node);
227231 self.buffer_index +%= 1;
228232 self.buffer_len += 1;
......@@ -283,6 +287,29 @@ test "std.event.Channel" {
283287 await putter;
284288}
285289
290test "std.event.Channel wraparound" {
291
292 // TODO provide a way to run tests in evented I/O mode
293 if (!std.io.is_async) return error.SkipZigTest;
294
295 const channel_size = 2;
296
297 var buf : [channel_size]i32 = undefined;
298 var channel: Channel(i32) = undefined;
299 channel.init(&buf);
300 defer channel.deinit();
301
302 // add items to channel and pull them out until
303 // the buffer wraps around, make sure it doesn't crash.
304 var result : i32 = undefined;
305 channel.put(5);
306 testing.expectEqual(@as(i32, 5), channel.get());
307 channel.put(6);
308 testing.expectEqual(@as(i32, 6), channel.get());
309 channel.put(7);
310 testing.expectEqual(@as(i32, 7), channel.get());
311}
312
286313async fn testChannelGetter(channel: *Channel(i32)) void {
287314 const value1 = channel.get();
288315 testing.expect(value1 == 1234);