aboutsummaryrefslogtreecommitdiff
path: root/lib/std/debug.zig
diff options
context:
space:
mode:
authorLemonBoy <thatlemon@gmail.com>2020-03-13 17:55:40 +0100
committerLemonBoy <thatlemon@gmail.com>2020-03-13 17:55:40 +0100
commitedcf8e0636c5a2674ce2b3e568929232c8cc61eb (patch)
tree33d953153c8e39e379459de2ae15de17ed4de319 /lib/std/debug.zig
parentaa49f972d655eb61ca899f76ba37933254f780a2 (diff)
downloadzig-edcf8e0636c5a2674ce2b3e568929232c8cc61eb.tar.gz
zig-edcf8e0636c5a2674ce2b3e568929232c8cc61eb.zip
std: Multithreaded-aware panic handler
Gracefully handle the case of several threads panicking at the same time.
Diffstat (limited to 'lib/std/debug.zig')
-rw-r--r--lib/std/debug.zig52
1 files changed, 41 insertions, 11 deletions
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();
}