SignalSafety
last edited April 17, 2009 19:27:18 (67.218.102.123)
| |
|---|---|
| Edit / History / New / Search | Quick Links: Home / Recent Changes / Glossary / Jobs / Forums / Help |
|
CocoaGlossary:Concepts (Mac OS X) - Writing signal-safe code
Signals are a common UNIX facility which are used to signal a process about certain events. Common signals that programmers see are SIGSEGV (segmentation fault), SIGBUS (bus error), SIGABRT (abort program), SIGTSTP (stop process, what happens when you press ^Z in Terminal), and many others. It's possible to install a signal handler on most signals, so that a function gets called when the signal is delivered. This can be used so that the user can make your program reload its configuration files (by trapping SIGHUP), to take action when writing to a pipe that's been closed (SIGPIPE), and for many other useful tasks. This page is about writing signal handlers, which is a way to execute code when signals are delivered. If you're getting a signal that you don't understand or your application is terminating unexpectedly, see SignalsSentOnCrash. Signals are delivered asynchronously, much like interrupts, and so there are a great deal of restrictions placed on the code which runs. Many of these restrictions are much like ThreadSafety, in that you have to account for the fact that your signal handler could run at any moment while other code is running, causing weird problems. Here is an example of code which is safe:
Just like with threads, if the signal handler is writing to a primitive and the main function is only reading, everything is safe. Well, as long as no one else sends a SIGHUP: if that were to happen, then the above example has the potential of a race condition. Now for a broken example:
This is broken for the same reason that it would be broken with two threads. The operation
If the signal handler is invoked while the main function is in the middle of this update process, an increment will be lost. Worse, the read and write of
If we were working with threads, then everything would work as expected. But with signals, this not only fails to solve the problem, but it actually makes it worse. Why? Threads run more or less simultaneously. On a multi-processor system they might really run simultaneously, but even on a single-processor system, the OS makes sure that every thread gets a chance to run. So while a thread might get stuck in the while loop for a while, eventually the other thread will get a chance to run, and it will unlock the lock. Signals, however, don't run simultaneously. While the signal handler is running, the main program is completely stopped. If the handler is invoked while the main program has locked the lock, the handler will spin forever waiting for the lock to be unlocked, while at the same time the main program is stuck waiting for the handler to end. Deadlock! If this situation ever happens, your program will completely freeze. So now we see that signal-safe code is even more restricted than thread-safe code. How do we fix it? For our example program, we can fix it by adding an auxiliary variable, like so:
Here, we have a sort of asymmetric lock. Instead of waiting for the lock to be free, the signal handler uses it to decide which variable to increment. The main program then manipulates the lock to ensure that it can always reliably read or modify the variable it's interested in while it performs the sum of the two. But you say, this counter is nonsense. I just want to print out a notice that my signal was received, nothing more. I'll just write this code:
This code is fine, right? None of this nonsense with locks or counters or anything. No! It's not safe because you're calling
What do we do? In this case, we'll have to do everything manually using functions which we know to be safe. In this case, we take advantage of the fact that
The key word is "async-signal safe". If you see a function documented as being "thread safe", you know that you can call it simultaneously from multiple threads. If you see a function documented as being "async-signal safe", then you know that you can call it from a signal handler without blowing up your program. A fairly complete list of signal safe functions can be found in
The trick is that a lot of code is not async-signal safe. Since it's so much harder to write, very little code is async-signal safe. For example, How do you get anything accomplished in a signal handler, then? The best bet is usually to do as little as possible in the handler itself, but somehow signal the rest of your program that something needs to be done. For example, let's say you want to reload your configuration file when sent a SIGHUP. If your program never blocks for long, we could write our program like this:
What if your program often blocks on input or a socket or something of that nature? All is not lost, however, because delivering a signal will automatically unblock your program if it's in the middle of a blocking
Looks good, right? If your program is blocked in the
The key is the
What do we do about this? The best way is to use a signaling mechanism which can be integrated into the If you do use this scheme, you have to be careful. (I can hear you saying, "What now???") Make sure to set your pipe to be non-blocking, otherwise your pipe could fill up in the signal handler before its emptied in your main program, and you'll deadlock. If the write fails because the pipe is full then that's fine anyway, because that means there's already a signal sitting in the pipe ready to be received the next time you go through your app's main loop.
What about signals in Cocoa? In fact, a Cocoa application's runloop, managed by CFRunLoop?, is much like the basic
SignalSafety is also related to writing proper code in the child process after calling | |
| Edit / History / New / Search | Quick Links: Home / Recent Changes / Glossary / Jobs / Forums / Help |