authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-01-06 10:50:45+00:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-01-06 10:50:45+00:00
log111165513156d5732d85e5ccb52b9d8bded41ffa
treed92c2b23c5a611a3383cc4fe3fd441d1724fe831
parent9d08eba2e111e96133b51c14aca7dc071920b6d2
signaturelock-open Commit is signed but in an unrecognized format.

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.

1 files changed, 8 insertions(+), 0 deletions(-)

lib/std/debug.zig+8
...@@ -532,6 +532,10 @@ pub fn defaultPanic(msg: []const u8, first_trace_addr: ?usize) noreturn {...@@ -532,6 +532,10 @@ pub fn defaultPanic(msg: []const u8, first_trace_addr: ?usize) noreturn {
532 else => {},532 else => {},
533 }533 }
534534
535 // Don't try to cancel during a panic. No need to re-enable cancelation,
536 // because the panic handler doesn't return.
537 _ = std.Options.debug_io.swapCancelProtection(.blocked);
538
535 if (enable_segfault_handler) {539 if (enable_segfault_handler) {
536 // If a segfault happens while panicking, we want it to actually segfault, not trigger540 // If a segfault happens while panicking, we want it to actually segfault, not trigger
537 // the handler.541 // the handler.
...@@ -1533,6 +1537,10 @@ fn handleSegfault(addr: ?usize, name: []const u8, opt_ctx: ?CpuContextPtr) noret...@@ -1533,6 +1537,10 @@ fn handleSegfault(addr: ?usize, name: []const u8, opt_ctx: ?CpuContextPtr) noret
1533}1537}
15341538
1535pub fn defaultHandleSegfault(addr: ?usize, name: []const u8, opt_ctx: ?CpuContextPtr) noreturn {1539pub fn defaultHandleSegfault(addr: ?usize, name: []const u8, opt_ctx: ?CpuContextPtr) noreturn {
1540 // Don't try to cancel during a segfault. No need to re-enable cancelation,
1541 // because the segfault handler doesn't return.
1542 _ = std.Options.debug_io.swapCancelProtection(.blocked);
1543
1536 // There is very similar logic to the following in `defaultPanic`.1544 // There is very similar logic to the following in `defaultPanic`.
1537 switch (panic_stage) {1545 switch (panic_stage) {
1538 0 => {1546 0 => {