user623990
user623990

Reputation:

Reading from a file descriptor in a non-blocking way with Perl

Let's say I have this:

pipe(READ,WRITE);
$pid = fork();
if ($pid == 0) {
    close(READ);
    # do something that may be blocking
    print WRITE "done";
    close(WRITE);
    exit(0);
} else {
    close(WRITE);
    $resp = <READ>;
    close(READ);
    # do other stuff
}

In this situation, it's possible for the child to hang indefinitely. Is there a way I can read from READ for a certain amount of time (ie, a timeout) and if I don't get anything, I proceed in the parent with the assumption that the child is hanging?

Upvotes: 1

Views: 492

Answers (2)

Anonymouss
Anonymouss

Reputation: 21

Typically, in C or Perl, you use select() to test if there is any input available. You can specify a timeout of 0 if you like, though used 1 second in the example below.:

use IO::Select;
pipe(READ,WRITE);
$s = IO::Select->new();
$s->add(\*READ);
$pid = fork();
if ($pid == 0) {
    close(READ);
    # do something that may be blocking
    for $i (0..2) {
        print "child - $i\n";
        sleep 1;
    }
    print WRITE "donechild";
    close(WRITE);
    print "child - end\n";
    exit(0);
} else {
    print "parent - $pid\n";
    close(WRITE);
    for $i (0..10) {
        print "parent - $i\n";
            # 1 second wait (timeout) here.  Can be 0.
        print "parent - ", (@r=$s->can_read(1))?"yes":"no", "\n";
        last if @r;
    }

    $resp = <READ>;
    print "parent - read: $resp\n";
    close(READ);
    # do other stuff
} 

Upvotes: 1

David W.
David W.

Reputation: 107040

Is there a way I can read from READ for a certain amount of time (ie, a timeout) and if I don't get anything, I proceed in the parent with the assumption that the child is hanging?

When you fork, you are working with two entirely separate processes. You're running two separate copies of your program. Your code cannot switch back and forth between the parent and child in your program. You're program is either the parent or the child.

You can use alarm in the parent to send a SIGALRM to your parent process. If I remember correctly, you set your $SIG{ALRM} subroutine, start your alarm, do your read, and then set alarm back to zero to shut it off. The whole thing needs to be wrapped in an eval.

I did this once a long time ago. For some reason, I remember that the standard system read didn't work. You have to use sysread. See Perl Signal Processing for more help.

Upvotes: 0

Related Questions