Context

Signal handling in POE suffers from many known problems. This document combines notes from six signals-related documents and supersedes them.

Summary

These reforms will improve the way programs can handle signals and their robustness when doing so. The changes will introduce incompatibilities with previous versions of POE. If this concerns you, see /Change Procedures for our "best practices" regarding incompatible changes.

Unified To-Do List / Change Schedule

The changes are described in detail afterwards.

  ? = Maybe.  An idea without a plan.
  - = Planned.
  = = Started.  Actively being worked on.
  + = Almost done.
  * = Done.  Hooray!
  X = Canceled.
  # = Blocked.  Someone or something is in the way.
* POE 0.20 (2002-06-07)
* retval: Document signal handler return value deprecation. (2002-05-09)
* retval: Add sig_handled() to replace significant return values. (2002-05-09)
* retval: Notify mailing list of intent to deprecate. (2002-05-09)
* _signal: Documented _default/_signal signal handler deprecation. (2002-05-11)
* dispatch: Notify mailing list of intent to deprecate. (2002-04-12)
* POE 0.24 (2002-12-09)
* retval: Upgrade signal handler return values to mandatory warnings.
* _signal: Upgrade use of _signal to mandatory warnings.
* dispatch: Upgrade old semantics to mandatory warnings.
* POE 0.27 (>= 2003-01-06)
* retval: Upgraded mandatory warnings to errors. (2003-05-20)
* _signal: Upgraded mandatory warnings to errors. (2003-05-20)
* dispatch: Upgrade mandatory warnings to errors. (2003-05-20)
* POE 0.30 (>= 2004-09-08)
This release will be characterized by the complete removal of deprecated signal handling semantics. The _signal error still exists to push problems on the user (no bizarre runtime problems).
* retval: Remove all support/errors for deprecated semantics.
* _signal: Remove all support for deprecated semantics.
* dispatch: Remove all support/errors for deprecated semantics.
* don't register signal handlers all the time (only when needed; fixes SIGTSTP/^Z issues)
? POE ?.?? (>= ????-??-??)
This release will be characterized by the reintroduction of Perl %SIG handlers for Perl 5.8.0 and above.
? Use Perl's signal handlers in 5.8.0 and above.
Perl 5.8 introduces "safe" signal handling. We can take advantage of that by enabling all signals (even SIGWINCH). This essentially forks development, however, into pre-5.8 and post-5.8 signal handling systems.
 
The current signal handling systems are not as fast as we'd like, but we've been avoiding native 5.8 support because of backward compatibility reasons and to avoid the extra maintenance work.
? Replace the SIGCHLD poll loop with $SIG{CHLD} when $] >= 5.8
? Re-enable SIGWINCH when $] >= 5.8
? Undetermined.
? Allow wheels, especially POE::Wheel::Run, to handle signals.
This task is probably dead in light of POE::Component::Child. The component performs similar actions as POE::Wheel::Run, but it does them in a separate session. As a result it can handle SIGCHLD, obviating the need for SIGCHLD support in POE::Wheel::Run.

Detailed Descriptions

Perl's signal handling is not safe.

Stable versions of perl before 5.8 do not handle signals well. In fact, they tend to dump core after a while. One of POE's design goals is to be robust and stable over long run times. Even minor corruption due to unsafe signal handling will eventually kill a long-running program. That's not tolerable.

Perl 5.8 is the first stable version of Perl that handles signals safely. There are some drawbacks to the way it achieves this. For example, system calls aren't interrupted by signals, so it's possible for them not to be delivered to a program in a timely fashion.

Backward compatibility is being maintained here. The existing 5.6-ish workarounds for unsafe signals also perform adequately under Perl 5.8.

Another solution is to use the Event module in conjunction with POE. Event purports to handle signals safely, and POE takes advantage of this when used with Event.

An alternative to Event or %SIG would be to poll for signals. POE already polls for SIGCHLD, so a lot of support code for this exists. Polling for signals trades reliability for safety. That is, it's possible to miss duplicate signals between polls.

Signal handler return values should not be significant.

Signal event handlers are the only kind that have siginificant return values. This is an inconsistency in POE's current design that throws a lot of people off.

The _default handler can also catch signals. In those cases, _default's return value is significant. This level of misdirection can significantly obscure the reason for signal strangeness.

We are phasing in a new function, sig_handled(), to explicitly flag which signals are handled. This will replace the problematic return value.

Signal handlers can prevent POE from stopping.

POE::Kernel must fire _signal whenever any signal is received. This requires POE to register handlers for every signal, whether or not a program is interested in it.

Combined with the ease of handling signals accidentally, this results in a lot of programs that must be shut down with kill -9 or similar means. That's downright unfriendly at best and often frustrating.

The _signal event is being phased out, to be replaced by explicit signal handlers via sig(). POE will only register %SIG handlers for signals a program is interested in, and then it clearly becomes the responsibility of the program to do the right things with them.

Wheels cannot register signal handlers.

Wheels don't exist as sessions of their own. Rather, they are bundles of event handlers which are dynamically loaded and unloaded in sessions.

Wheel::Run needs to register at least two signal handlers for ideal operation: SIGWINCH to catch (and propagate) window-size changes to child processes. SIGCHLD to catch exit codes and note abnormal exits.

Problems occur when a session has its own handlers for those signals. If Wheel::Run were to register new ones, they would clobber the existing ones.

One solution is to allow multiple signal handlers per signal, per session. That is, let a single session have two or more WINCH handlers, each going to a separate sub.

This could be done with a new API, similar to the newer alarms functions. The new watcher function would return a unique ID that the other functions could refer to.

$id = signal_watch(SIGNAL_NAME => EVENT_NAME);

$status = signal_ignore(SIGNAL_ID);

Multiple signals can be registered for the same SIGNAL_NAME, and each will have its own SIGNAL_ID. Signals watched in wheels and in the larger sessions will be kept separate.

A second solution is to leave signals limited and focus on a child-process management component instead. It would manage many Wheel::Run objects, and it would augment them with its own signal handlers.

_default and _signal won't handle signals.

The _default and _signal events will stop handling signals once sig() must be used to register handlers.

We are phasing out _signal altogether. Sessions that want to handle signals can explicitly register handlers for them.

It's still possible to catch signals via _default, but the signals would need to be explicitly watched with sig() to begin with. That's not a problem.

Signal fatality semantics are not useful.

Currently signals have the opportunity to shut down individual sessions without regard to the program's needs as a whole. For example, some component authors don't handle signals, and their components will shut down on SIGINT. On the other hand, applications as a whole may want to trap SIGINT and not shut down. Having components shut down in that case is dead wrong, since the application is left in an inconsistent state.

Several of the preceding points help to rectify the situation.

One aspect that hasn't been covered, though, is how to make signal handling an all-or-none proposition. This is another aspect of signal reforms we are working on.

Essentially, when one session handles a signal, the program as a whole is considered to have done so. Any sessions affected by the signal do not shut down because of it.

They still receive the signal notification, though, which gives them the opportunity to voluntarily shut down.