diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/9p_io.zig | 8 | ||||
| -rw-r--r-- | src/detached/server.zig | 2 | ||||
| -rw-r--r-- | src/fs.zig | 5 | ||||
| -rw-r--r-- | src/gui/gui.zig | 2 | ||||
| -rw-r--r-- | src/macos.zig | 2 | ||||
| -rw-r--r-- | src/pardes.zig | 16 | ||||
| -rw-r--r-- | src/tty/tty.zig | 2 |
7 files changed, 29 insertions, 8 deletions
diff --git a/src/9p_io.zig b/src/9p_io.zig index 2ca11a87..6bd03049 100644 --- a/src/9p_io.zig +++ b/src/9p_io.zig @@ -296,7 +296,15 @@ pub const Listener = struct { // Set with the turn still held, so `reset` sees it and waits. if (restoring) l.restore_writer.store(true, .release); defer if (restoring) l.restore_writer.store(false, .release); + core.fs.late_failure_len = 0; if (core.effects_len != 0) pardes.turn.awaitSettled(epoch); + // ponytail: one slot, so a failure of another client's effects that + // settle in the same wait is told to this write too. + if (l.core == core and core.fs.late_failure_len != 0) { + const failed = pardes.ctlfs.failText(req.tag, pardes.ctlfs.E.IO, core.fs.late_failure[0..core.fs.late_failure_len]); + pardes.ctlfs.events.noteError(core, req, failed); + return conn.reply(&failed, ""); + } // The Restore's own write is answered once it is done, and before // `reset` hangs every connection up, this one too, so the writer // hears that it happened rather than a cut; a failed one leaves the diff --git a/src/detached/server.zig b/src/detached/server.zig index 5374bf73..3921d8d9 100644 --- a/src/detached/server.zig +++ b/src/detached/server.zig @@ -555,7 +555,7 @@ pub const Session = struct { fn writeFile(ctx: ?*anyopaque, pane: u8, path: []const u8, bytes: []const u8) void { const s = of(ctx); filesystem.write(s.core, path, bytes) catch |err| - return s.core.saveFailed(pane, "save", err); + return s.core.saveFailed(pane, path, err); if (s.core.panes[pane]) |pn| if (pn.file) |f| if (std.mem.eql(u8, f.path, path)) { if (s.watches[pane]) |*w| if (w.serial == pn.serial) switch (w.generation) { .text => w.generation = .{ .text = std.hash.Wyhash.hash(0, bytes) }, @@ -1263,6 +1263,11 @@ pub const Namespace = struct { refused: bool = false, failure: [256]u8 = undefined, failure_len: u16 = 0, + /// What went wrong performing a write's effects after it was answered + /// by the core (a save the host could not do): the 9P write, which + /// waits for them, fails with it (src/9p_io.zig). + late_failure: [256]u8 = undefined, + late_failure_len: u16 = 0, /// A refusal that quotes the message it refuses, as Plan 9's cmderror /// does (kernel/misc/parse.c:82); answered at once (src/9p_io.zig). ename: [320]u8 = undefined, diff --git a/src/gui/gui.zig b/src/gui/gui.zig index 5bc1ea6d..84ec1c5c 100644 --- a/src/gui/gui.zig +++ b/src/gui/gui.zig @@ -4422,7 +4422,7 @@ fn killJob(ctx: ?*anyopaque, pane: u8) bool { fn writeFile(ctx: ?*anyopaque, pane: u8, path: []const u8, bytes: []const u8) void { const s = shellOf(ctx); filesystem.write(s.core, path, bytes) catch |err| - return s.core.saveFailed(pane, "save", err); + return s.core.saveFailed(pane, path, err); if (s.core.panes[pane]) |pane_state| if (pane_state.file) |f| { if (std.mem.eql(u8, f.path, path)) if (s.watches[pane]) |*w| if (w.serial == pane_state.serial) switch (w.generation) { diff --git a/src/macos.zig b/src/macos.zig index b472e6cc..b180d7ba 100644 --- a/src/macos.zig +++ b/src/macos.zig @@ -2487,7 +2487,7 @@ fn killJob(ctx: ?*anyopaque, pane: u8) bool { fn writeFile(ctx: ?*anyopaque, pane: u8, path: []const u8, bytes: []const u8) void { const st = hostState(ctx); filesystem.write(st.core, path, bytes) catch |err| - return st.core.saveFailed(pane, "save", err); + return st.core.saveFailed(pane, path, err); st.file_watches.restampText(pane, path, std.hash.Wyhash.hash(0, bytes)); var mbuf: [256]u8 = undefined; st.core.setMessage(pane, message.stamp(&mbuf, "saved", path)); diff --git a/src/pardes.zig b/src/pardes.zig index f559b125..581f616e 100644 --- a/src/pardes.zig +++ b/src/pardes.zig @@ -2261,7 +2261,7 @@ test "a save the host could not do leaves the pane dirty" { const Refusing = struct { fn writeFile(ctx: ?*anyopaque, pane: u8, _: []const u8, _: []const u8) void { const core: *Pardes = @ptrCast(@alignCast(ctx.?)); - core.saveFailed(pane, "save", error.PermissionDenied); + core.saveFailed(pane, "/x/y.txt", error.PermissionDenied); } }; const p = try Pardes.init(gpa, .{ .tty_only = true, .cols = 100, .rows = 30 }); @@ -4906,15 +4906,23 @@ pub const Pardes = struct { if (p.host.vtable.write_file) |f| return f(p.host.ctx, pane, path, bytes); // The in-process filesystem reports the same way a real host does, so // an OOM here leaves the pane dirty rather than looking saved. - if (!p.fallback.writeFile(path, bytes)) p.saveFailed(pane, "save", error.OutOfMemory); + if (!p.fallback.writeFile(path, bytes)) p.saveFailed(pane, path, error.OutOfMemory); } - pub fn saveFailed(p: *Pardes, id: u8, what: []const u8, err: anyerror) void { + /// A write of `path` the host could not do: the pane stays dirty, the + /// message row says `Save <path>: <why>`, and the 9P write that asked + /// for it, waiting on the save, fails with that (`fs.late_failure`). + pub fn saveFailed(p: *Pardes, id: u8, path: []const u8, err: anyerror) void { if (p.panes[id]) |pane| if (pane.file) |*f| { // The `-%` spelling fs.zig already uses for "make this dirty". f.saved_revision = f.revision -% 1; }; - p.reportError(id, what, err); + var what: [limits.host_path_cap + 8]u8 = undefined; + p.reportError(id, std.fmt.bufPrint(&what, "Save {s}", .{path}) catch "Save", err); + const said = if (p.panes[id]) |pane| pane.msg[0..pane.msg_len] else "Save failed"; + const kept = @import("Messages.zig").clip(said, p.fs.late_failure.len); + @memcpy(p.fs.late_failure[0..kept.len], kept); + p.fs.late_failure_len = @intCast(kept.len); } /// The path a watch is about: a real file's, or a PDF's. diff --git a/src/tty/tty.zig b/src/tty/tty.zig index 822c2d7f..d17316e8 100644 --- a/src/tty/tty.zig +++ b/src/tty/tty.zig @@ -1314,7 +1314,7 @@ const Shell = struct { fn writeFile(ctx: ?*anyopaque, pane: u8, path: []const u8, bytes: []const u8) void { const s = of(ctx); filesystem.write(s.core, path, bytes) catch |err| - return s.core.saveFailed(pane, "save", err); + return s.core.saveFailed(pane, path, err); if (s.core.panes[pane]) |pn| if (pn.file) |f| if (std.mem.eql(u8, f.path, path)) { if (s.watches[pane]) |*w| if (w.serial == pn.serial) switch (w.generation) { .text => w.generation = .{ .text = std.hash.Wyhash.hash(0, bytes) }, |
