Search code examples
perlasynchronousforkfcntl

How can I set a pipe to O_NONBLOCK perl


This works fine:

#!/usr/bin/perl -w
# 

#pipe2 - use pipe and fork so child can send to parent

use IO::Handle;

pipe(READER, WRITER);
WRITER->autoflush(1);

if ($pid = fork) {
    close WRITER;


    chomp($line = <READER>);
    print "Parent Pid $$ just read this: `$line'\n";
    close READER;
    waitpid($pid,0);
} else {
    die "cannot fork: $!" unless defined $pid;
    close READER;
    print WRITER "Child Pid $$ is sending this\n";
    close WRITER;  # this will happen anyway
    exit;
}

But when I try to make the reader not block with fcntl as this:

use IO::Handle;
use Fcntl;

pipe(READER, WRITER);
WRITER->autoflush(1);

if ($pid = fork) {
    close WRITER;
    fcntl(fileno(READER),F_GETFL,$flags)
        or die "Couldn't get flags for READER : $!\n";
    $flags |= O_NONBLOCK;
    fcntl(fileno(READER), F_SETFL, $flags)
        or die "Couldn't set flags for READER $!\n";
    chomp($line = <READER>);
    print "Parent Pid $$ just read this: `$line'\n";
    close READER;
    waitpid($pid,0);
} else {
    die "cannot fork: $!" unless defined $pid;
    close READER;
    print WRITER "Child Pid $$ is sending this\n";
    close WRITER;  # this will happen anyway
    exit;
}

I get:

fcntl() on unopened filehandle 3 at pip2.pl line 14.
Couldn't get flags for READER : Bad file descriptor

I need to "watch" the child and do something if it doesn't respond properly within a specific time. I need async communications with the child.


Solution

  • fcntl(fileno(READER),F_GETFL,$flags)
    

    fcntl gets a filehandle, not a file number. Use fcntl(READER,... not fcntl(fileno(READER), ....

    Apart from that it is recommended to not use global symbols for file handles. Better use local variables, i.e.

     pipe(my $reader, my $writer); 
     $writer->autoflush();
     ...
     
    

    Apart from not being in potential conflict to other global symbols and avoiding risks of uncaught typos, this will also close the respective file handles ones the variable gets out of scope.