From edcf8e0636c5a2674ce2b3e568929232c8cc61eb Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Fri, 13 Mar 2020 17:55:40 +0100 Subject: std: Multithreaded-aware panic handler Gracefully handle the case of several threads panicking at the same time. --- lib/std/debug.zig | 52 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 11 deletions(-) (limited to 'lib/std/debug.zig') diff --git a/lib/std/debug.zig b/lib/std/debug.zig index 0a7a0dee7e..cdbba2367b 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -235,9 +235,17 @@ pub fn panic(comptime format: []const u8, args: var) noreturn { panicExtra(null, first_trace_addr, format, args); } -/// TODO multithreaded awareness +/// Non-zero whenever the program triggered a panic. +/// The counter is incremented/decremented atomically. var panicking: u8 = 0; +// Locked to avoid interleaving panic messages from multiple threads. +var panic_mutex = std.Mutex.init(); + +/// Counts how many times the panic handler is invoked by this thread. +/// This is used to catch and handle panics triggered by the panic handler. +threadlocal var panic_stage: usize = 0; + pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, comptime format: []const u8, args: var) noreturn { @setCold(true); @@ -247,25 +255,47 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c resetSegfaultHandler(); } - switch (@atomicRmw(u8, &panicking, .Add, 1, .SeqCst)) { + switch (panic_stage) { 0 => { - const stderr = getStderrStream(); - noasync stderr.print(format ++ "\n", args) catch os.abort(); - if (trace) |t| { - dumpStackTrace(t.*); + panic_stage = 1; + + _ = @atomicRmw(u8, &panicking, .Add, 1, .SeqCst); + + // Make sure to release the mutex when done + { + const held = panic_mutex.acquire(); + defer held.release(); + + const stderr = getStderrStream(); + noasync stderr.print(format ++ "\n", args) catch os.abort(); + if (trace) |t| { + dumpStackTrace(t.*); + } + dumpCurrentStackTrace(first_trace_addr); + } + + if (@atomicRmw(u8, &panicking, .Sub, 1, .SeqCst) != 1) { + // Another thread is panicking, wait for the last one to finish + // and call abort() + + // XXX: Find a nicer way to loop forever + while (true) {} } - dumpCurrentStackTrace(first_trace_addr); }, 1 => { - // TODO detect if a different thread caused the panic, because in that case - // we would want to return here instead of calling abort, so that the thread - // which first called panic can finish printing a stack trace. - warn("Panicked during a panic. Aborting.\n", .{}); + panic_stage = 2; + + // A panic happened while trying to print a previous panic message, + // we're still holding the mutex but that's fine as we're going to + // call abort() + const stderr = getStderrStream(); + noasync stderr.print("Panicked during a panic. Aborting.\n", .{}) catch os.abort(); }, else => { // Panicked while printing "Panicked during a panic." }, } + os.abort(); } -- cgit v1.2.3 From e496ef26dabb3a2da4821c7c0c2e4ffc6f7d86ce Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Fri, 13 Mar 2020 18:40:18 +0100 Subject: Nicer idle wait loop Trying to acquire twice the same mutex generates an idle loop. --- lib/std/debug.zig | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'lib/std/debug.zig') diff --git a/lib/std/debug.zig b/lib/std/debug.zig index cdbba2367b..f2c736bbba 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -278,8 +278,11 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c // Another thread is panicking, wait for the last one to finish // and call abort() - // XXX: Find a nicer way to loop forever - while (true) {} + // Here we sleep forever without hammering the CPU by causing a + // deadlock + var deadlock = std.Mutex.init(); + _ = deadlock.acquire(); + _ = deadlock.acquire(); } }, 1 => { -- cgit v1.2.3 From 2501e80500f99c1ede6daf2f7c50c2dec3c9675c Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Fri, 13 Mar 2020 19:20:18 +0100 Subject: Even better idle waiting method --- lib/std/debug.zig | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'lib/std/debug.zig') diff --git a/lib/std/debug.zig b/lib/std/debug.zig index f2c736bbba..5600990924 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -278,11 +278,11 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c // Another thread is panicking, wait for the last one to finish // and call abort() - // Here we sleep forever without hammering the CPU by causing a - // deadlock - var deadlock = std.Mutex.init(); - _ = deadlock.acquire(); - _ = deadlock.acquire(); + // Sleep forever without hammering the CPU + var event = std.ResetEvent.init(); + event.wait(); + + unreachable; } }, 1 => { -- cgit v1.2.3