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 {
235235 panicExtra(null, first_trace_addr, format, args);
236236}
237237
238/// TODO multithreaded awareness
238/// Non-zero whenever the program triggered a panic.
239/// The counter is incremented/decremented atomically.
239240var 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
241249pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, comptime format: []const u8, args: var) noreturn {
242250 @setCold(true);
243251
......@@ -247,25 +255,47 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c
247255 resetSegfaultHandler();
248256 }
249257
250 switch (@atomicRmw(u8, &panicking, .Add, 1, .SeqCst)) {
258 switch (panic_stage) {
251259 0 => {
252 const stderr = getStderrStream();
253 noasync stderr.print(format ++ "\n", args) catch os.abort();
254 if (trace) |t| {
255 dumpStackTrace(t.*);
260 panic_stage = 1;
261
262 _ = @atomicRmw(u8, &panicking, .Add, 1, .SeqCst);
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) {}
256283 }
257 dumpCurrentStackTrace(first_trace_addr);
258284 },
259285 1 => {
260 // TODO detect if a different thread caused the panic, because in that case
261 // we would want to return here instead of calling abort, so that the thread
262 // which first called panic can finish printing a stack trace.
263 warn("Panicked during a panic. Aborting.\n", .{});
286 panic_stage = 2;
287
288 // A panic happened while trying to print a previous panic message,
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();
264293 },
265294 else => {
266295 // Panicked while printing "Panicked during a panic."
267296 },
268297 }
298
269299 os.abort();
270300}
271301