Skip to content

Commit ea76946

Browse files
committed
Io.Queue: fix empty and full states being indistinguishable.
1 parent c603d27 commit ea76946

File tree

2 files changed

+64
-64
lines changed

2 files changed

+64
-64
lines changed

lib/std/Io.zig

Lines changed: 43 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,3 +208,24 @@ test "select" {
208208
},
209209
}
210210
}
211+
212+
fn 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+
225+
test "Queue" {
226+
try testQueue(1);
227+
try testQueue(2);
228+
try testQueue(3);
229+
try testQueue(4);
230+
try testQueue(5);
231+
}

0 commit comments

Comments
 (0)