ealeon
ealeon

Reputation: 12452

perl: doesnt exit without pressing "enter"

use threads;
use threads::shared;
use Term::ReadKey;

sub input_worker {

    local $SIG{'KILL'} = sub { threads->exit(0); return;};

    while (1) {
        if (defined(my $char = ReadKey 0, *STDIN)) {
            print "$char";
        }
    }

    return;
} ## end sub input_worker

my $in_thr = threads->create(\&input_worker);
sleep 5;
my $rc = $in_thr->kill('KILL')->join();
print "$rc\n";

This program wont exit by itself and just hangs. It will only exit after pressing "enter"

How can i make it so that it will exit by itself after 'kill' is signaled

P.S. I dont want to use detach();

Upvotes: 1

Views: 112

Answers (1)

Sobrique
Sobrique

Reputation: 53478

Mixing signals and threads is a bit of a challenge, simply because $thread->kill doesn't actually use real signals (since signals are sent to processes, not threads). Which is just as well, because if it did, SIGKILL can break things.

Even when talking about 'normal' signals - you will have slightly unexpected behaviour, because of perl's handling of them See: perlipc . It's not impossible to use, but you need to be aware of the caveats involved.

But the root of the problem is - that the signal is handled safely, which means perl waits until a suitable moment to process it. It will not do this during a 'read' operation, so the signal processing will get deferred.

I think likely what is happening here is your ReadKey is not a nonblocking operation like you think it is. With reference to the Term::ReadKey manpage

ReadKey MODE [, Filehandle] Takes an integer argument, which can currently be one of the following values:

 0    Perform a normal read using getc
-1   Perform a non-blocked read
>0   Perform a timed read

So what it does instead is - starts reading from STDIN, and blocks (ignoring signals) until you press enter. At which point it runs your signal handler.

Given what you're trying to do here - I would suggest you don't want to use signals at all, and instead - just use a shared variable.

Something like this:

use threads;
use threads::shared;
use Term::ReadKey;

my $done : shared; 

sub input_worker {
    while ( not $done ) {
        if ( defined( my $char = ReadKey( -1, *STDIN ) ) ) {
            print "$char";
        }
    }
    return;
} ## end sub input_worker

my $in_thr = threads->create( \&input_worker );
sleep 10;
$done++;
my $rc = $in_thr->join();
print "$rc\n";

Will terminate after the timeout, and because it's doing nonblocking reads, it'll bail out on time, without input. You should note though - this thread is going to be 'spinning' looping rapidly waiting to see if input has been pressed - so it's not very CPU efficient. (a small delay in the cycle helps immensely there, say 0.1s).

Upvotes: 5

Related Questions