I've got a moderately complex program which I'm switching to an event-loop
architecture, using the Event module. I'm doing something slightly
naughty with signal handlers, and I'd like advice on how best to do it
with Event.
Before I started using Event, I had a couple of signal handlers that,
as part of their work, locally modified the handling of their signal.
The interesting case is with SIGTSTP: after doing some application
cleanup, it would temporarily reset SIGTSTP to default handling and
unblock it and then deliver itself a SIGTSTP. The point of this is
that the behaviour looks, from the user's point of view, just like an
ordinary ^Z suspension, but the application cleanup gets done along
the way. After the program is restarted, the signal handler restores
the catching of SIGTSTP and does more bookkeeping before returning.
With Event, I'd like the signal handler to operate synchronously as part
of the event loop. (The issue of the signal handler possibly being
called at an inopportune moment is one of the reasons for moving to
an event-loop architecture.) If I do it in the obvious way, replacing
"$SIG{TSTP} =" with "Event->signal signal =>", then my signal handler
gets called fine, but it fails to restore proper signal handling after
temporarily switching it to default handling.
I realise that in that I'm breaking one of the documented rules: "A given
signal can be handled by %SIG or Event, but not both.". I understand
why it doesn't work: %SIG doesn't reflect the actual signal handler, so
my "local $SIG{TSTP}" ends up `restoring' undef to $SIG{TSTP} instead
of restoring the actual SIGTSTP handler. I want to make it work, in
some form.
I see four main possible ways for me to achieve the effect I want:
0. Have my own stub signal handler in %SIG, instead of Event handling it.
The stub signal handler would do nothing more than trigger an event,
the callback for which is the real signal handler that mucks about
with %SIG. Result: I get synchronous signal handling, while still
retaining control over %SIG. This is inelegant; I'd rather have
Event handle the signal directly when I don't need to usurp it.
1. Rewrite my invoke-default-handler function in C, so that it can save
and restore the real struct sigaction rather than being limited to
what is visible in %SIG. This is cleaner, but means I'd lose some
portability. The invoke-default-handler function is actually of wider
utility than just this program, so I might implement this anyway so
that it can cope with other situations where %SIG isn't the whole
story, but I'm concerned about the portability loss for this program.
2. Arrange for Event to be able to cede and retake control of signal
handlers. It's a simple enough thing to negotiate. At minimum, Event
would need to provide just two new functions: "do you [Event] currently
handle signal FOO?" and "rehook signal FOO". This would allow for
a much wider range of cases where it is desired to temporarily take
over control of a signal from Event.
3. Arrange for %SIG to show *something* for a non-Perl signal handler,
so that such handlers can be manipulated by Perl code in the expected
manner. (This would actually make my non-working code work without
modification.) I'm not sure whether it could be done safely in the
general case -- one has to ensure that the non-Perl signal handler
can actually be safely used when Perl code tries to install it via
%SIG. Even if it's not possible in general, it might be possible
for cooperating code to do it, so Event could use that mechanism to
achieve option 2 with the means of negotiation being %SIG.
Being an engineer, I'm leaning towards attempting 1, 2, and 3
simultaneously. Each of them would be useful even with the others
being available, and of course TMTOWTDI. Not knowing Event's internals,
though, I'd like commentary from the list.
-zefram