authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2025-11-03 21:05:18-05:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2025-11-04 04:24:32-05:00
logea76946d2af588d135e79a97f19d128acfeb8915
treef4d800b03a8497afa6fcb87aa080e4462a41369f
parentc603d27f900540b52586ee2638060a4b2ac7c557

Io.Queue: fix empty and full states being indistinguishable.


2 files changed, 64 insertions(+), 64 deletions(-)

lib/std/Io.zig+43-64
......@@ -1238,8 +1238,8 @@ pub const TypeErasedQueue = struct {
12381238
12391239 /// Ring buffer. This data is logically *after* queued getters.
12401240 buffer: []u8,
1241 put_index: usize,
1242 get_index: usize,
1241 start: usize,
1242 len: usize,
12431243
12441244 putters: std.DoublyLinkedList,
12451245 getters: std.DoublyLinkedList,
......@@ -1260,8 +1260,8 @@ pub const TypeErasedQueue = struct {
12601260 return .{
12611261 .mutex = .init,
12621262 .buffer = buffer,
1263 .put_index = 0,
1264 .get_index = 0,
1263 .start = 0,
1264 .len = 0,
12651265 .putters = .{},
12661266 .getters = .{},
12671267 };
......@@ -1286,6 +1286,16 @@ pub const TypeErasedQueue = struct {
12861286 };
12871287 }
12881288
1289 fn puttableSlice(q: *const TypeErasedQueue) ?[]u8 {
1290 const unwrapped_index = q.start + q.len;
1291 const wrapped_index, const overflow = @subWithOverflow(unwrapped_index, q.buffer.len);
1292 const slice = switch (overflow) {
1293 1 => q.buffer[unwrapped_index..],
1294 0 => q.buffer[wrapped_index..q.start],
1295 };
1296 return if (slice.len > 0) slice else null;
1297 }
1298
12891299 fn putLocked(q: *TypeErasedQueue, io: Io, elements: []const u8, min: usize, uncancelable: bool) Cancelable!usize {
12901300 // Getters have first priority on the data, and only when the getters
12911301 // queue is empty do we start populating the buffer.
......@@ -1306,20 +1316,12 @@ pub const TypeErasedQueue = struct {
13061316 return elements.len;
13071317 }
13081318
1309 {
1310 const available = q.buffer[q.put_index..];
1311 const copy_len = @min(available.len, remaining.len);
1312 @memcpy(available[0..copy_len], remaining[0..copy_len]);
1313 remaining = remaining[copy_len..];
1314 q.put_index += copy_len;
1315 if (remaining.len == 0) return elements.len;
1316 }
1317 {
1318 const available = q.buffer[0..q.get_index];
1319 const copy_len = @min(available.len, remaining.len);
1320 @memcpy(available[0..copy_len], remaining[0..copy_len]);
1319 while (q.puttableSlice()) |slice| {
1320 const copy_len = @min(slice.len, remaining.len);
1321 assert(copy_len > 0);
1322 @memcpy(slice[0..copy_len], remaining[0..copy_len]);
1323 q.len += copy_len;
13211324 remaining = remaining[copy_len..];
1322 q.put_index = copy_len;
13231325 if (remaining.len == 0) return elements.len;
13241326 }
13251327
......@@ -1354,46 +1356,32 @@ pub const TypeErasedQueue = struct {
13541356 };
13551357 }
13561358
1359 fn gettableSlice(q: *const TypeErasedQueue) ?[]const u8 {
1360 const overlong_slice = q.buffer[q.start..];
1361 const slice = overlong_slice[0..@min(overlong_slice.len, q.len)];
1362 return if (slice.len > 0) slice else null;
1363 }
1364
13571365 fn getLocked(q: *@This(), io: Io, buffer: []u8, min: usize, uncancelable: bool) Cancelable!usize {
13581366 // The ring buffer gets first priority, then data should come from any
13591367 // queued putters, then finally the ring buffer should be filled with
13601368 // data from putters so they can be resumed.
13611369
13621370 var remaining = buffer;
1363 if (q.get_index <= q.put_index) {
1364 const available = q.buffer[q.get_index..q.put_index];
1365 const copy_len = @min(available.len, remaining.len);
1366 @memcpy(remaining[0..copy_len], available[0..copy_len]);
1367 q.get_index += copy_len;
1371 while (q.gettableSlice()) |slice| {
1372 const copy_len = @min(slice.len, remaining.len);
1373 assert(copy_len > 0);
1374 @memcpy(remaining[0..copy_len], slice[0..copy_len]);
1375 q.start += copy_len;
1376 if (q.buffer.len - q.start == 0) q.start = 0;
1377 q.len -= copy_len;
13681378 remaining = remaining[copy_len..];
13691379 if (remaining.len == 0) {
13701380 q.fillRingBufferFromPutters(io);
13711381 return buffer.len;
13721382 }
1373 } else {
1374 {
1375 const available = q.buffer[q.get_index..];
1376 const copy_len = @min(available.len, remaining.len);
1377 @memcpy(remaining[0..copy_len], available[0..copy_len]);
1378 q.get_index += copy_len;
1379 remaining = remaining[copy_len..];
1380 if (remaining.len == 0) {
1381 q.fillRingBufferFromPutters(io);
1382 return buffer.len;
1383 }
1384 }
1385 {
1386 const available = q.buffer[0..q.put_index];
1387 const copy_len = @min(available.len, remaining.len);
1388 @memcpy(remaining[0..copy_len], available[0..copy_len]);
1389 q.get_index = copy_len;
1390 remaining = remaining[copy_len..];
1391 if (remaining.len == 0) {
1392 q.fillRingBufferFromPutters(io);
1393 return buffer.len;
1394 }
1395 }
13961383 }
1384
13971385 // Copy directly from putters into buffer.
13981386 while (q.putters.popFirst()) |putter_node| {
13991387 const putter: *Put = @alignCast(@fieldParentPtr("node", putter_node));
......@@ -1410,6 +1398,7 @@ pub const TypeErasedQueue = struct {
14101398 q.fillRingBufferFromPutters(io);
14111399 return buffer.len;
14121400 }
1401
14131402 // Both ring buffer and putters queue is empty.
14141403 const total_filled = buffer.len - remaining.len;
14151404 if (total_filled >= min) return total_filled;
......@@ -1432,30 +1421,20 @@ pub const TypeErasedQueue = struct {
14321421 fn fillRingBufferFromPutters(q: *TypeErasedQueue, io: Io) void {
14331422 while (q.putters.popFirst()) |putter_node| {
14341423 const putter: *Put = @alignCast(@fieldParentPtr("node", putter_node));
1435 {
1436 const available = q.buffer[q.put_index..];
1437 const copy_len = @min(available.len, putter.remaining.len);
1438 @memcpy(available[0..copy_len], putter.remaining[0..copy_len]);
1424 while (q.puttableSlice()) |slice| {
1425 const copy_len = @min(slice.len, putter.remaining.len);
1426 assert(copy_len > 0);
1427 @memcpy(slice[0..copy_len], putter.remaining[0..copy_len]);
1428 q.len += copy_len;
14391429 putter.remaining = putter.remaining[copy_len..];
1440 q.put_index += copy_len;
14411430 if (putter.remaining.len == 0) {
14421431 putter.condition.signal(io);
1443 continue;
1444 }
1445 }
1446 {
1447 const available = q.buffer[0..q.get_index];
1448 const copy_len = @min(available.len, putter.remaining.len);
1449 @memcpy(available[0..copy_len], putter.remaining[0..copy_len]);
1450 putter.remaining = putter.remaining[copy_len..];
1451 q.put_index = copy_len;
1452 if (putter.remaining.len == 0) {
1453 putter.condition.signal(io);
1454 continue;
1432 break;
14551433 }
1434 } else {
1435 q.putters.prepend(putter_node);
1436 break;
14561437 }
1457 q.putters.prepend(putter_node);
1458 break;
14591438 }
14601439 }
14611440};
lib/std/Io/test.zig+21
......@@ -208,3 +208,24 @@ test "select" {
208208 },
209209 }
210210}
211
212fn testQueue(comptime len: usize) !void {
213 const io = testing.io;
214 var buf: [len]usize = undefined;
215 var queue: Io.Queue(usize) = .init(&buf);
216 var begin: usize = 0;
217 for (1..len + 1) |n| {
218 const end = begin + n;
219 for (begin..end) |i| try queue.putOne(io, i);
220 for (begin..end) |i| try expect(try queue.getOne(io) == i);
221 begin = end;
222 }
223}
224
225test "Queue" {
226 try testQueue(1);
227 try testQueue(2);
228 try testQueue(3);
229 try testQueue(4);
230 try testQueue(5);
231}