CDahn CDahn - 5 months ago 20
Perl Question

Does join in perl threads block SIGALRM?

I have a small sample program that hangs on perl 5.16.3. I am attempting to use an

alarm
to trigger if two threads don't finish working in time in a much more complicated program, but this boils down the gist of it. I know there's plenty of other ways to do this, but for the sake of argument, let's say I'm stuck with the code the way it is. I'm not sure if this is a bug in perl, or something that legitimately shouldn't work.

I have researched this on the Internet, and it seems like mixing alarms and threads is generally discouraged, but I've seen plenty of examples where people claim that this is a perfectly reasonable thing to do, such as this other SO question, Perl threads with alarm. The code provided in the accepted answer on that question also hangs on my system, which is why I'm wondering if maybe this is something that's now broke, at least as of 5.16.3.

It appears that in the code below, if I call
join
before the
alarm
goes off, the
alarm
never triggers. If I replace the
join
with
while(1){}
and go into a busy-wait loop, then the
alarm
goes off just fine, so it appears that
join
is blocking the SIGALRM for some reason.

My expectation is that the
join
happens, and then a few seconds later I see "Alarm!" printed on the screen, but this never happens, so long as that
join
gets called before the
alarm
goes off.

#!/usr/bin/env perl

use strict;
use warnings;
use threads;

sub worker {
print "Worker thread started.\n";
while(1){}
}

my $thread = threads->create(\&worker);

print "Setting alarm.\n";
$SIG{ALRM} = sub { print "Alarm!\n" };
alarm 2;

print "Joining.\n";
$thread->join();

Answer

The problem has nothing to do with threads. Signals are only processed between Perl ops, and join is written in C, so the signal will only be handled when join returns. The following demonstrates this:

#!/usr/bin/env perl

use strict;
use warnings;
use threads;

sub worker {
    print "Worker thread started.\n";
    for (1..5) {
       sleep(1);
       print(".\n");
    }
}

my $thread = threads->create(\&worker);

print "Setting alarm.\n";
$SIG{ALRM} = sub { print "Alarm!\n" };
alarm 2;

print "Joining.\n";
$thread->join();

Output:

Setting alarm.
Joining.
Worker thread started.
.
.
.
.
.
Alarm!

join is essentially a call to pthread_join. Unlike other blocking system calls, pthread_join does not get interrupted by signals.


By the way, I renamed $tid to $thread since threads->create returns a thread object, not a thread id.

Comments