std: Multithreaded-aware panic handler

Gracefully handle the case of several threads panicking at the same
time.
This commit is contained in:
LemonBoy 2020-03-13 17:55:40 +01:00
parent aa49f972d6
commit edcf8e0636

View File

@ -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();
}