This context is not captured within the handler but before,
during the normal usage of the app.
That’s still quite tricky. You have to protect this data structure from concurrent access, because the signal handler might fire while you’re updating it. And you can’t use a mutex for that, because the signal handler might fire regardless of whether the updating thread is holding that mutex.
To make this work you need an atomic. Consider this general outline:
-
Maintain the data structure in the normal way, if necessary using a mutex.
-
Then flatten it into a pointer-sized snapshot for the benefit of your signal handler.
-
Atomically replace the old snapshot with the new.
-
In the signal handler, work from the current snapshot. Remember that this read needs to be atomic.
But even this isn’t as easy as it sounds. Consider this sequence:
-
The signal handler fires on thread A.
-
It reads the snapshot and starts working on it.
-
Thread B, which continues to run even though you’re in a signal handler, replaces the snapshot with a new one.
-
And then deallocates the old one.
At this point the signal handler is working with deallocated memory.
There are ways around this but none of them are straightforward:
-
You could have the signal handler suspend all the other threads before it starts working from the snapshot. But that introduces its own complexities, for example, how do you avoid deadlock if two threads crash at the same time.
-
You could create a more elaborate data structure. The key term here is lock free. If you search the ’net for that, you’ll find lots of info about it. However, you’re in a particularly bad place because of the signal handler constraints.
Finally … I should implement a second handler using signal
or
sigaction
?
If you’re doing a crash reporter, ignore Dispatch sources and use only sigaction
.
Finally, this is on the Mac right? If so, you have a much better option: Do this in a separate process. So, spawn a child process and have it monitor the app process. When the app process dies, the child process writes out the app process’s state. This is in a separate process, and thus has none of the constraints associated with a signal handler.
The traditional challenge with such an approach is getting state out of the crashed process. That involves Mach exception handlers. Given the choice between signal handlers, Mach exception handlers, and gnawing my own leg off, I’m not 100% sure which way I’d go (-:
However, in this case that’s not needed because you only want to capture app state. Thus, you could use the snapshot approach I’ve described above but put the result into memory that you share between the app and this child process. The mechanics of this are still tricky, but it’s likely to be more reliable than anything you do in process. Moreover, there’s no way it could disrupt the Apple crash reporter.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"