Srikanth Jeeva
Srikanth Jeeva

Reputation: 3011

Perl STDIN hangs in background, works fine in foreground

This is my program

my $input;
my $finish = 0;
my $timer = 5; # 5 seconds
eval {
    while( ! $finish ) {
        local $SIG{ALRM} = sub {
          # check counter and set alarm again
          if (--$timer) { alarm 1 }
          # no more waiting
          else { die "timeout getting the input \n" }
        };
        # alarm every second
        alarm 1;
        $input = <STDIN>;
        alarm 0;
        if ( $input ) {
            chomp $input;
            if( $input ){
                print( "Received input : $input" );
                $finish = 1;
            } else {
                print( "Please enter valid input" );
            }
        } else {
            print( "input is undefined" );
            last;
        }
    }
};

if ($@ || !$input) {
    print( "Timeout in getting input." );
    return undef;
}

return $input;

STDIN works fine in foreground. But fails when running the same program background. The logic is to go out of the loop if user did not enter any input within 5 seconds. But when running in background, the process should exit within x seconds but the process gets stuck in line <STDIN>.

How to fix this?

Upvotes: 2

Views: 171

Answers (1)

hobbs
hobbs

Reputation: 239682

When a backgrounded process attempts terminal input, it gets sent the signal SIGTTIN, which has a default action of "stop" — i.e. the process is paused, the same as if it had received SIGSTOP. Since it's stopped, it doesn't get a chance to process its alarm, or exit, or do anything else.

So, you're going to want to set a SIGTTIN handler to override that default stop behavior.

The simplest thing you could do would be local $SIG{TTIN} = 'IGNORE'; which would cause signal to be ignored. The read of STDIN would then immediately fail (returns undef with $! set to EIO), and you would hit the "input is undefined" case in your code.

You could also set local $SIG{TTIN} = sub { die "process is backgrounded" }; which would let you distinguish that case more clearly.

Or you could set up something clever with a signal handler and retrying the read, so that in case the user decided to background and re-attach before the timeout expired, they would still be able to provide input, but I leave that up to you.

Upvotes: 3

Related Questions