| ... | @@ -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 | } |
| 237 | | 237 | |
| 238 | /// TODO multithreaded awareness | 238 | /// Non-zero whenever the program triggered a panic. |
| | 239 | /// The counter is incremented/decremented atomically. |
| 239 | var panicking: u8 = 0; | 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 | pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, comptime format: []const u8, args: var) noreturn { | 249 | pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, comptime format: []const u8, args: var) noreturn { |
| 242 | @setCold(true); | 250 | @setCold(true); |
| 243 | | 251 | |
| ... | @@ -247,25 +255,50 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c | ... | @@ -247,25 +255,50 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c |
| 247 | resetSegfaultHandler(); | 255 | resetSegfaultHandler(); |
| 248 | } | 256 | } |
| 249 | | 257 | |
| 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 | // Sleep forever without hammering the CPU |
| | 282 | var event = std.ResetEvent.init(); |
| | 283 | event.wait(); |
| | 284 | |
| | 285 | unreachable; |
| 256 | } | 286 | } |
| 257 | dumpCurrentStackTrace(first_trace_addr); | | |
| 258 | }, | 287 | }, |
| 259 | 1 => { | 288 | 1 => { |
| 260 | // TODO detect if a different thread caused the panic, because in that case | 289 | panic_stage = 2; |
| 261 | // we would want to return here instead of calling abort, so that the thread | 290 | |
| 262 | // which first called panic can finish printing a stack trace. | 291 | // A panic happened while trying to print a previous panic message, |
| 263 | warn("Panicked during a panic. Aborting.\n", .{}); | 292 | // we're still holding the mutex but that's fine as we're going to |
| | 293 | // call abort() |
| | 294 | const stderr = getStderrStream(); |
| | 295 | noasync stderr.print("Panicked during a panic. Aborting.\n", .{}) catch os.abort(); |
| 264 | }, | 296 | }, |
| 265 | else => { | 297 | else => { |
| 266 | // Panicked while printing "Panicked during a panic." | 298 | // Panicked while printing "Panicked during a panic." |
| 267 | }, | 299 | }, |
| 268 | } | 300 | } |
| | 301 | |
| 269 | os.abort(); | 302 | os.abort(); |
| 270 | } | 303 | } |
| 271 | | 304 | |