╭ changed ───────────╮ │src/serve_common.zig│ ╰────────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────╮ │24 return .{ .ok = try std.fs.path.join(allocator, &.{ repo_root_path, &event_id_hex }) }; │ │25 } │ │26 │ │27 -pub fn logError(err: *std.Io.Writer, comptime fmt: []const u8, args: anytype) void { │ │27 +// every connection task shares one error writer, so writing to it takes a │ │28 +// lock. uncancelable, since a log line shouldn't be a cancelation point. │ │29 +var log_mutex: std.Io.Mutex = .init; │ │30 + │ │31 +pub fn logError(io: std.Io, err: *std.Io.Writer, comptime fmt: []const u8, args: anytype) void {│ │32 + log_mutex.lockUncancelable(io); │ │33 + defer log_mutex.unlock(io); │ │34 err.print(fmt, args) catch return; │ │35 err.flush() catch {}; │ │36 } │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │70 while (true) { │ │71 const stream = self.net_server.accept(self.io) catch |accept_err| { │ │72 if (accept_err == error.Canceled) return; │ │67 - logError(self.err, "{s} accept failed: {s}\n", .{ self.name, @errorName(accept_err) }); │ │73 + logError(self.io, self.err, "{s} accept failed: {s}\n", .{ self.name, @errorName(accept_err) });│ │74 continue; │ │75 }; │ │76 self.tasks.async(self.io, Conn.run, .{Conn{ .context = self.context, .stream = stream }}); │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯