summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ninep/events.zig35
1 files changed, 30 insertions, 5 deletions
diff --git a/src/ninep/events.zig b/src/ninep/events.zig
index c0b52d03..a8c5022f 100644
--- a/src/ninep/events.zig
+++ b/src/ninep/events.zig
@@ -102,10 +102,13 @@ pub const Queue = struct {
/// One record per read; `.again` holds the read until a record arrives.
pub fn readQueue(p: *Pardes, req: Req, q: *Queue) Reply {
const record = q.peek() orelse return .{ .tag = req.tag, .status = .again };
- if (req.size < record.len) return tree.failText(req.tag, E.INVAL, "invalid read: shorter than the record waiting");
+ // A read shorter than the record takes its front, and the next read the
+ // rest, as acme's event file and pty/data allow: a shell's `read`
+ // takes a byte at a time.
+ const n = @min(record.len, req.size);
const out = p.fs.stage(p.gpa);
- out.appendSlice(p.gpa, record) catch return Reply.fail(req.tag, E.NOMEM);
- q.pop();
+ out.appendSlice(p.gpa, record[0..n]) catch return Reply.fail(req.tag, E.NOMEM);
+ q.popFront(n);
return .{ .tag = req.tag, .payload = .{ .staged = @intCast(out.items.len) } };
}
@@ -905,8 +908,9 @@ test "event records are acme's bytes, one per read, and .again when empty" {
try testing.expectEqual(Status.again, rd(p, event, 0, 4096).reply.status);
_ = noteAction(p, 0, .body_look, 0, 3, flag_filename, "one");
- try testing.expectEqual(E.INVAL, rd(p, event, 0, 4).errno());
- try testing.expectEqualStrings("ML0 3 4 3 one\n", rd(p, event, 0, 4096).bytes);
+ // A short read takes the record's front; the next, the rest.
+ try testing.expectEqualStrings("ML0 ", rd(p, event, 0, 4).bytes);
+ try testing.expectEqualStrings("3 4 3 one\n", rd(p, event, 0, 4096).bytes);
const big = "z" ** max_record_text;
_ = noteAction(p, 0, .body_exec, 0, max_record_text, 0, big);
@@ -994,6 +998,27 @@ test "one event reader at a time, and a writer still holds the pane" {
_ = call(p, .{ .tag = 19, .op = .release, .node = event, .handle = next.reply.handle });
}
+test "an event record read a byte at a time arrives whole, in order, as a shell's read takes it" {
+ const p = try withFile(testing.allocator, "x\n");
+ defer p.deinit();
+ const serial = serialOf(p);
+ const event = Node.of(serial, .event);
+ const h = call(p, .{ .tag = 1, .op = .open, .node = event }).reply.handle;
+ defer _ = call(p, .{ .tag = 9, .op = .release, .node = event, .handle = h });
+ _ = wr(p, Node.of(serial, .exec), "Msg piecewise\n");
+ _ = wr(p, Node.of(serial, .exec), "Msg second\n");
+ var got: [128]u8 = undefined;
+ var n: usize = 0;
+ while (n < got.len) {
+ const piece = call(p, .{ .tag = 2, .op = .read, .node = event, .handle = h, .off = n, .size = 1 });
+ if (piece.reply.status != .ok or piece.bytes.len == 0) break;
+ got[n] = piece.bytes[0];
+ n += 1;
+ if (std.mem.count(u8, got[0..n], "\n") == 2) break;
+ }
+ try testing.expectEqualStrings("FX0 0 1 13 Msg piecewise\nFX0 0 1 10 Msg second\n", got[0..n]);
+}
+
test "a pane deleted while its event file is open leaves no suppression behind" {
const gpa = testing.allocator;
const p = try withFile(gpa, "x\n");