Catching 2 signals with the same code causes hang?
Doing What Sounds Right <[email protected]>
| Newsgroups | gmane.comp.lang.perl.poe |
|---|---|
| Message-ID | <[email protected]> |
Ok, so I officially broke the time-space continuum today.
I'm writing a PoCo::IRC bot for an IRC server that I frequent, partly to
help them out, and partly as a learning experience in writing OO Perl.
Recently, I ran into a brick wall while trying to catch signals coming
from the operating system. I got POE to catch HUP, KILL, TERM, and INT
and whenever it receives INT or TERM, to call a sub to "gracefully" (I
don't know how graceful most people would consider graceful) shut the
bot down with a nice little quit message and everything. I had a
momentary swelling of ego, until I tried the code to handle SIGINT. I
noticed that the bot hung whenever it handled SIGINT (I'd have to send a
second SIGINT to completely kill it). After a bit of help with dumping
some debugging information from perigrin, I got a stack trace, which
looks like this:
<dt> Cannot resolve ``2'' into a session reference at /usr/local/lib/perl5/site_perl/5.8.5/POE/Component/IRC.pm line 1906
POE::Component::IRC::yield('POE::Component::IRC::State=HASH(0x86857c4)', 'privmsg', '#test', 'NOTICE->Shutting down... (command given by system) (Reason: R...') called at ./main.pl line 1099
main::putlog('Shutting down... (command given by system) (Reason: Received ...', 'notice') called at ./main.pl line 700
main::closedown('system', 'SIGINT', 'POE::Kernel=ARRAY(0x829d530)', 'Received SIGINT from the system') called at ./main.pl line 135
main::on_int('INT') called at /usr/local/lib/perl5/site_perl/5.8.5/POE/Loop/Select.pm line 310
eval {...} called at /usr/local/lib/perl5/site_perl/5.8.5/POE/Loop/Select.pm line 310
POE::Kernel::loop_do_timeslice('POE::Kernel=ARRAY(0x829d530)') called at /usr/local/lib/perl5/site_perl/5.8.5/POE/Loop/Select.pm line 333
POE::Kernel::loop_run('POE::Kernel=ARRAY(0x829d530)') called at /usr/local/lib/perl5/site_perl/5.8.5/POE/Kernel.pm line 1072
POE::Kernel::run('POE::Kernel=ARRAY(0x829d530)') called at ./main.pl line 80
Died at /usr/local/lib/perl5/site_perl/5.8.5/POE/Kernel.pm line 321.
Died at /usr/local/lib/perl5/site_perl/5.8.5/POE/Kernel.pm line 321.
The code involved in closing down the bot is as follows:
These are the subs handling catching INT and TERM:
sub on_int
{
closedown("system", "SIGINT" , $poe_kernel , "Received SIGINT from the system");
}
sub on_term
{
closedown("system", "SIGTERM" , $poe_kernel , "Received SIGTERM from the system");
}
This is closedown():
sub closedown
{
my ( $nick, $command, $kernel, $text ) = @_;
putlog( "Shutting down... (command given by $nick" . ") (Reason: $text" . ")", "notice" );
$kernel->alarm_remove_all( );
unlink $bot{pidfile};
$irc->yield( shutdown => uc($command) . " from $nick" . ": $text" . "." );
}
This is the sub that contains line 1099:
sub putlog
{
# What are we logging, and what level is it?
my ( $text, $level ) = @_[ 0, 1 ];
# Convert the text level into a numeric for later filtering
my $lnum;
if ( $level eq "debug" ) { $lnum = 1; }
if ( $level eq "info" ) { $lnum = 2; }
if ( $level eq "notice" ) { $lnum = 3; }
if ( $level eq "warning" ) { $lnum = 4; }
if ( $level eq "error" ) { $lnum = 5; }
# If our level is greater than or equal to the level we want logged..
if ( $lnum >= $bot{loglevel} )
{
# Put stuff to $bot{logfile}
open( LOGFILE, ">> $bot{logfile}" );
print( LOGFILE ts() . " -" . uc($level) . "- " . $text . "\n" );
close(LOGFILE);
}
# Default to $bot{loglevel} if our channel log level is not specified.
if ( !$bot{monchan_level} )
{
$bot{monchan_level} = $bot{loglevel};
}
# Check our channel specific level, and output to channel if we want the events logged.
if ( $lnum >= $bot{monchan_level} )
{
$irc->yield( privmsg => $bot{monchan_chan} => uc($level) . "->" . $text );
}
}
perigrin suggested changing this line:
$irc->yield( privmsg => $bot{monchan_chan} => uc($level) . "->" . $text );
to this:
$irc->call( privmsg => $bot{monchan_chan} => uc($level) . "->" . $text );
because call will block, while yield won't. This seems to fix the problem.
I'm just wondering what I'm doing (or not doing) that would cause POE to
act like this. As I said before, handling SIGTERM (with on_term) works
whether I use $irc->yield() or $irc->call(), but handling SIGINT (with
on_int) only works if I use $irc->call().
PS $irc is my PoCo::IRC object.