authorgravatar for thatlemon@gmail.comLemonBoy <thatlemon@gmail.com> 2020-03-13 17:55:40+01:00
committergravatar for thatlemon@gmail.comLemonBoy <thatlemon@gmail.com> 2020-03-13 17:55:40+01:00
logedcf8e0636c5a2674ce2b3e568929232c8cc61eb
tree33d953153c8e39e379459de2ae15de17ed4de319
parentaa49f972d655eb61ca899f76ba37933254f780a2

std: Multithreaded-aware panic handler

Gracefully handle the case of several threads panicking at the same time.

1 files changed, 41 insertions(+), 11 deletions(-)

lib/std/debug.zig+41-11
...@@ -235,9 +235,17 @@ pub fn panic(comptime format: []const u8, args: var) noreturn {...@@ -235,9 +235,17 @@ pub fn panic(comptime format: []const u8, args: var) noreturn {
235 panicExtra(null, first_trace_addr, format, args);235 panicExtra(null, first_trace_addr, format, args);
236}236}
237237
238/// TODO multithreaded awareness238/// Non-zero whenever the program triggered a panic.
239/// The counter is incremented/decremented atomically.
239var panicking: u8 = 0;240var panicking: u8 = 0;
240241
242// Locked to avoid interleaving panic messages from multiple threads.
243var panic_mutex = std.Mutex.init();
244
245/// Counts how many times the panic handler is invoked by this thread.
246/// This is used to catch and handle panics triggered by the panic handler.
247threadlocal var panic_stage: usize = 0;
248
241pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, comptime format: []const u8, args: var) noreturn {249pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, comptime format: []const u8, args: var) noreturn {
242 @setCold(true);250 @setCold(true);
243251
...@@ -247,25 +255,47 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c...@@ -247,25 +255,47 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c
247 resetSegfaultHandler();255 resetSegfaultHandler();
248 }256 }
249257
250 switch (@atomicRmw(u8, &panicking, .Add, 1, .SeqCst)) {258 switch (panic_stage) {
251 0 => {259 0 => {
252 const stderr = getStderrStream();260 panic_stage = 1;
253 noasync stderr.print(format ++ "\n", args) catch os.abort();261
254 if (trace) |t| {262 _ = @atomicRmw(u8, &panicking, .Add, 1, .SeqCst);
255 dumpStackTrace(t.*);263
264 // Make sure to release the mutex when done
265 {
266 const held = panic_mutex.acquire();
267 defer held.release();
268
269 const stderr = getStderrStream();
270 noasync stderr.print(format ++ "\n", args) catch os.abort();
271 if (trace) |t| {
272 dumpStackTrace(t.*);
273 }
274 dumpCurrentStackTrace(first_trace_addr);
275 }
276
277 if (@atomicRmw(u8, &panicking, .Sub, 1, .SeqCst) != 1) {
278 // Another thread is panicking, wait for the last one to finish
279 // and call abort()
280
281 // XXX: Find a nicer way to loop forever
282 while (true) {}
256 }283 }
257 dumpCurrentStackTrace(first_trace_addr);
258 },284 },
259 1 => {285 1 => {
260 // TODO detect if a different thread caused the panic, because in that case286 panic_stage = 2;
261 // we would want to return here instead of calling abort, so that the thread287
262 // which first called panic can finish printing a stack trace.288 // A panic happened while trying to print a previous panic message,
263 warn("Panicked during a panic. Aborting.\n", .{});289 // we're still holding the mutex but that's fine as we're going to
290 // call abort()
291 const stderr = getStderrStream();
292 noasync stderr.print("Panicked during a panic. Aborting.\n", .{}) catch os.abort();
264 },293 },
265 else => {294 else => {
266 // Panicked while printing "Panicked during a panic."295 // Panicked while printing "Panicked during a panic."
267 },296 },
268 }297 }
298
269 os.abort();299 os.abort();
270}300}
271301