For some reason threads doesn't seem to be able to find the $SIG{'KILL') signal. I got the code from a comment here http://www.perlmonks.org/index.pl?node_id=557219 This is on Windows.
use strict;
use warnings;
use threads;
sub test_sub{
$SIG{'KILL'} = sub { threads->exit(); };
while(1){}
}
my $new_thread = threads->create('test_sub');
$new_thread->kill('KILL')->detach();
returns
Signal KILL received in thread 1, but no signal handler set. at test1.pl line 13.
Perl exited with active threads:
1 running and unjoined
0 finished and unjoined
0 running and detached
Edit: looks like I'm using threads version '1.43'. I don't know if that the problem but I'll try a newer version.
This appears to be some sort of race condition, and it works when there is more going on.
use warnings;
use strict;
use feature 'say';
use threads;
use threads::shared;
my $thread_handler_on :shared = 0;
sub test_handler {
say "\tIn thread.";
$SIG{'KILL'} = sub {
$thread_handler_on = 1;
say "\tSet flag, exiting the thread in handler";
threads->exit();
};
sleep 3;
say "\tSet handler";
sleep 1;
say "\tHandler done napping";
}
my $new_thread = threads->create( 'test_handler' );
sleep 1;
say "Sending 'kill' to thread";
$new_thread->kill('KILL')->detach();
sleep 5;
say "Did handler run: $thread_handler_on";
The code in the question is right out of threads docs (along with detach).
This prints
In thread.
Sending 'kill' to thread
Set flag, exiting the thread in handler
Did handler run: 1
Without those sleeps in the main thread I also get the reported behavior.
Note that these "signals" are not sent via kernel, per Threads signalling
The thread signalling capability provided by this module does not actually send signals via the OS. It emulates signals at the Perl-level such that signal handlers are called in the appropriate thread.
There are not one but two race conditions that could lead to the behaviour you observe:
You're sending the KILL signal before the child has a chance to setup the signal handler.
Below, I used synchronization to ensure the signal handler is created before the signal is sent.
You're exiting the program before the signal handler has a chance to call threads->exit.
Why do people insist on using ->detach?
Fixed:
use strict;
use warnings;
use feature qw( say );
use threads;
use threads::shared;
sub test_handler {
say sprintf "[%s %s] Starting thread", time, threads->tid;
say sprintf "[%s %s] Doing stuff", time, threads->tid;
sleep 4;
say sprintf "[%s %s] Done doing stuff", time, threads->tid;
say sprintf "[%s %s] Exiting thread", time, threads->tid;
}
sub create_thread(&) {
my ($thread_func) = @_;
# Reap any threads that might have exited.
$_->join() for threads->list(threads::joinable);
my $ready :shared = 0;
my $thread = async {
$SIG{KILL} = sub {
say sprintf "[%s %s] Forcibly exiting thread", time, threads->tid;
threads->exit();
};
# Signal creator that the thread is initialized.
{ lock $ready; $ready = 1; cond_signal($ready); }
$thread_func->();
};
# Wait for the thread to finish initializing.
{ lock $ready; while (!$ready) { cond_wait($ready); } }
return $thread;
}
my $thread = create_thread(\&test_handler);
say sprintf "[%s %s] Sending KILL signal", time, threads->tid;
$thread->kill('KILL');
say sprintf "[%s %s] Doing stuff", time, threads->tid;
sleep(2);
say sprintf "[%s %s] Done doing stuff", time, threads->tid;
# Wait for threads to exit.
say sprintf "[%s %s] Waiting for threads to exit", time, threads->tid;
$_->join() for threads->list();
say sprintf "[%s %s] Exiting", time, threads->tid;
Output:
[1513212149 1] Starting thread
[1513212149 1] Doing stuff
[1513212149 0] Sending KILL signal
[1513212149 0] Doing stuff
[1513212151 0] Done doing stuff
[1513212151 0] Waiting for threads to exit
[1513212153 1] Forcibly exiting thread
[1513212153 0] Exiting
IMPORTANT Note that the signal didn't interrupt sleep. No actual POSIX signal is sent[1] (since you can only send those to a process), so OS calls can't be interrupted. Instead, Perl actively checks if a signal was sent between most Perl opcodes[2]. But that means that long-running opcodes like sleep, m// and XS functions calls can delay the signal handler indefinitely.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With