commit 111165513156d5732d85e5ccb52b9d8bded41ffa (tree)
parent 9d08eba2e111e96133b51c14aca7dc071920b6d2
Author: Matthew Lugg <mlugg@mlugg.co.uk>
Date: Tue, 6 Jan 2026 10:50:45 +0000
std: block cancelation in default panic and segfault handlers
It doesn't make any sense for a task to be canceled while it's
panicking.
As a happy accident, this also solves some cases where safety panics in
`Io.Threaded` would cause stack traces not to print due to invalid
thread-local state: when cancelation is blocked, `Io.Threaded` doesn't
consult said thread-local state at all. For instance, try inserting a
panic just after a call to `Syscall.start()` in `Io.Threaded`, and then
call the `Io` function in question from a `concurrent` task. Before this
PR, the stack trace fails to print, because the panic handler sees the
thread-local cancelation state in an unexpected state, leading to a
recursive panic. After this PR, the stack trace prints fine.
Diffstat:
1 file changed, 8 insertions(+), 0 deletions(-)
diff --git a/lib/std/debug.zig b/lib/std/debug.zig
@@ -532,6 +532,10 @@ pub fn defaultPanic(msg: []const u8, first_trace_addr: ?usize) noreturn {
else => {},
}
+ // Don't try to cancel during a panic. No need to re-enable cancelation,
+ // because the panic handler doesn't return.
+ _ = std.Options.debug_io.swapCancelProtection(.blocked);
+
if (enable_segfault_handler) {
// If a segfault happens while panicking, we want it to actually segfault, not trigger
// the handler.
@@ -1533,6 +1537,10 @@ fn handleSegfault(addr: ?usize, name: []const u8, opt_ctx: ?CpuContextPtr) noret
}
pub fn defaultHandleSegfault(addr: ?usize, name: []const u8, opt_ctx: ?CpuContextPtr) noreturn {
+ // Don't try to cancel during a segfault. No need to re-enable cancelation,
+ // because the segfault handler doesn't return.
+ _ = std.Options.debug_io.swapCancelProtection(.blocked);
+
// There is very similar logic to the following in `defaultPanic`.
switch (panic_stage) {
0 => {