Async signals and GMainLoop



back on-list, as this is good info for everyone and will probably deserve to be in the FAQ. now that i've looked into the problem, i see that this question has been asked a time or two before on the list, but we couldn't reproduce it.

we have a long explanation, and three options at the end. everyone's input is invited.


On Thursday, February 19, 2004, at 08:02 PM, Sameer VK wrote:

When I tried setting a signal handler in Perl:
$SIG{INT} = \&app_cleanup, the script did not respond, probably
because the handler in the perl-Glib library cannot be overridden?

well, there's no handler, so it's not that.  if you stopped the
mainloop in your handler, that would explain why the gui stopped
responding.  beyond that, i need more info.  again, if you can't
divulge details publicly, just email me offlist, i'd be happy to help.

the perl script I tried is:

$SIG{INT} = sub{ warn "inside sighandler"; };
use Glib;
my $mainloop = Glib::MainLoop->new();
$mainloop->run;


async signal handlers can get called at any point in a program's execution, and therefore there are *very* few things you can safely do in them. basically, anything that calls malloc() is not safe, because malloc() may already be in the middle of something when your signal handler gets called --- so you typically do nothing beyond setting a new value to an existing integer variable.

because these handlers are so limited, perl >= 5.7.3 introduced deferred signal handlers. When you install something in %SIG, perl actually installs a dummy handler that sets a flag and exits; then at "strategic 'safe' points" in later execution, perl sees that flag is set and then runs the handler. this is documented in the section "Deferred Signals" of perlipc(1).

if you don't set a handler for a signal, perl just lets your os's normal signal handler run and everything is fine.


the key thing to note here, however is the "at strategic 'safe' points" *later* in the script's execution. that means control must return to the interpreter for your handler to run. in your test case, the SIGINT comes when the interpreter is blocking on the call to g_main_loop_run()... the interpreter never reaches the point where it sees that it needs to run the handler!

you can prove this:

        $SIG{INT} = sub{ warn "inside sighandler"; };
        use Glib;
        my $mainloop = Glib::MainLoop->new();
        Glib::Timeout->add (100, sub {1});
        $mainloop->run;

^C will then stop the program.

of course, it will stop the program up to 100ms *after* the signal was received. an idle may give you better granularity, however, recurring idles are a great way to increase your system load for no good reason; a timeout at about 100ms is less of a strain on your system, but even then it's wasted work.


you were using perl 5.8.0 (which is what was standard on redhat 8.0), and that version provides the deferred signals as a compile-time default. some later version, i think 5.8.1 but definitely by 5.8.3, allows you to disable the safe signals at runtime by setting PERL_SIGNALS=unsafe in the environment. with that, your script works unaltered.


after a fair bit of discussion in #gtk+, there are a few options:

a) if you are going to install a handler in %SIG and you need it to run during a main loop, ensure that you have a new enough version of perl and set PERL_SIGNALS=unsafe in the env before running.

b) install a no-op timeout that causes perl to check for signals every so often, say 100ms. pygtk does this. it's simple and it works, but it's a little ugly and wasteful.

c) yosh proposed an elegant but difficult-to-get-right solution involves using a tie to mask and override %SIG to allow us to catch modifications to the signal set, so we can override the signal handler to call a springboard, which queues up an event for the main loop to process, which will trigger perl to run its signal handlers. we will have to monitor %SIG because it may change over the course of the app, and we don't want to mask the os signals for no reason, nor do we want to categorically disable the deferred signal handling in all gtk2-perl apps, because the deferred signals are A Good Thing.


option c) is the most elegant and imposes no runtime penalty, but will be the hardest to get right. option b) is a fair, if dirty, stopgap. option a) is what we have at the moment.


what say you, peanut gallery?

--
elysse (in labor): is the head the biggest part?
midwife: yes.
elysse: oh, good.




[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]