| ... | ... | @@ -235,9 +235,17 @@ pub fn panic(comptime format: []const u8, args: var) noreturn { |
| 235 | 235 | panicExtra(null, first_trace_addr, format, args); |
| 236 | 236 | } |
| 237 | 237 | |
| 238 | | /// TODO multithreaded awareness |
| 238 | /// Non-zero whenever the program triggered a panic. |
| 239 | /// The counter is incremented/decremented atomically. |
| 239 | 240 | var panicking: u8 = 0; |
| 240 | 241 | |
| 242 | // Locked to avoid interleaving panic messages from multiple threads. |
| 243 | var 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. |
| 247 | threadlocal var panic_stage: usize = 0; |
| 248 | |
| 241 | 249 | pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, comptime format: []const u8, args: var) noreturn { |
| 242 | 250 | @setCold(true); |
| 243 | 251 | |
| ... | ... | @@ -247,25 +255,47 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c |
| 247 | 255 | resetSegfaultHandler(); |
| 248 | 256 | } |
| 249 | 257 | |
| 250 | | switch (@atomicRmw(u8, &panicking, .Add, 1, .SeqCst)) { |
| 258 | switch (panic_stage) { |
| 251 | 259 | 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) {} |
| 256 | 283 | } |
| 257 | | dumpCurrentStackTrace(first_trace_addr); |
| 258 | 284 | }, |
| 259 | 285 | 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(); |
| 264 | 293 | }, |
| 265 | 294 | else => { |
| 266 | 295 | // Panicked while printing "Panicked during a panic." |
| 267 | 296 | }, |
| 268 | 297 | } |
| 298 | |
| 269 | 299 | os.abort(); |
| 270 | 300 | } |
| 271 | 301 | |