http://qs321.pair.com?node_id=11123397

azadian has asked for the wisdom of the Perl Monks concerning the following question:

Here's the tiny test program to illustrate the problem:
#!/usr/bin/perl # teof2 20201104 NHA # Test eof() for IPC over socketpair filehandle. use diagnostics; use strict 'subs'; use strict 'refs'; use Socket; use IO::Handle; my $child; #filehandle to child process my $parent; #filehandle to parent process my $pid; #Process ID of child process socketpair($child, $parent, AF_UNIX, SOCK_STREAM, PF_UNSPEC) or die "socketpair: $! +"; $child->autoflush(1); $parent->autoflush(1); if ($pid = fork()) { #parent close $parent or die "close: $!\n"; sleep 1; print STDOUT time%1000, ": polling child\n"; if (eof($child)) { print STDOUT time%1000, ": no input from child yet\n"; } else { my $line = <$child>; chomp $line; print STDOUT time%1000, ": received <$line>\n"; } } else { #child die "cannot fork: $!" unless defined $pid; close $child or die "close: $!\n"; print STDOUT time%1000, ": child started\n"; sleep 5; print {$parent} time%1000, ": child printed\n"; close $parent or die "close: $!\n"; exit; }
Here's what I get when I run it:
= ./teof2 325: child started 326: polling child 330: received <330: child printed>
The only way I can understand this is that eof() is blocking until input appears, and then returning false. Am I missing something here?

Replies are listed 'Best First'.
Re: eof() blocking
by tybalt89 (Monsignor) on Nov 04, 2020 at 21:05 UTC

    From perldoc -f eof

    Note that this function actually reads a character and then "ungetc"s it, so isn't useful in an interactive context.
    What I would do instead:

    #!/usr/bin/perl use strict; # https://perlmonks.org/?node_id=11123397 use warnings; use Socket; my $child; #filehandle to child process my $parent; #filehandle to parent process my $pid; #Process ID of child process socketpair($child, $parent, AF_UNIX, SOCK_STREAM, PF_UNSPEC) or die "socketpair: $! +"; $child->autoflush(1); $parent->autoflush(1); if ($pid = fork()) { close $parent or die "close: $!\n"; sleep 1; print STDOUT time%1000, ": polling child\n"; my $buf = ''; while( sysread $child, $buf, 4096, length $buf ) { while( $buf =~ s/(.*)\n// ) { print STDOUT time%1000, ": received <$1>\n"; } } print STDOUT time%1000, ": end of input from child\n"; wait; } elsif( defined $pid ) { close $child or die "close: $!\n"; print STDOUT time%1000, ": child started\n"; sleep 5; print $parent time%1000, ": child printed\n"; close $parent or die "close: $!\n"; exit; } else { die "cannot fork: $!"; }
      Thanks for your input. I have a hard time regarding this as an "interactive" context, but I guess the moral of the story is that eof() only works if it's an honest-to-God file. The fact that it blocks seems to me completely contrary to the whole point of the function. The system IO is what I was hoping to avoid, but it looks like I can't.

        While I'm not completely sure of what you want, it appears to me you are looking for something like a "datawaiting()" call instead of an end-of-file.
        Here's a (somewhat) minimal change to your program with the end-of-file replaced :)

        #!/usr/bin/perl use diagnostics; use strict 'subs'; use strict 'refs'; use Socket; use IO::Handle; my $child; #filehandle to child process my $parent; #filehandle to parent process my $pid; #Process ID of child process socketpair($child, $parent, AF_UNIX, SOCK_STREAM, PF_UNSPEC) or die "socketpair: $! +"; $child->autoflush(1); $parent->autoflush(1); if ($pid = fork()) { #parent close $parent or die "close: $!\n"; sleep 1; print STDOUT time%1000, ": polling child\n"; if (not datawaiting($child)) { ##### CHANGE +D print STDOUT time%1000, ": no input from child yet\n"; } else { my $line = <$child>; chomp $line; print STDOUT time%1000, ": received <$line>\n"; } } else { #child die "cannot fork: $!" unless defined $pid; close $child or die "close: $!\n"; print STDOUT time%1000, ": child started\n"; sleep 5; print {$parent} time%1000, ": child printed\n"; close $parent or die "close: $!\n"; exit; } sub datawaiting ####### CHANGED see perldoc -f select { my $rin = my $win = my $ein = ''; vec($rin, fileno(shift), 1) = 1; return scalar select my $rout = $rin, my $wout = $win, my $eout = $e +in, 0; }

        Outputs:

        277: child started 278: polling child 278: no input from child yet

        Is that what you were looking for ?

        With IO::Select, this isn't too much to avoid, is it?

        #!/usr/bin/perl use diagnostics; use strict 'subs'; use strict 'refs'; use Socket; use IO::Handle; use IO::Select; #### ADDED my $child; #filehandle to child process my $parent; #filehandle to parent process my $pid; #Process ID of child process socketpair($child, $parent, AF_UNIX, SOCK_STREAM, PF_UNSPEC) or die "socketpair: $! +"; $child->autoflush(1); $parent->autoflush(1); if ($pid = fork()) { #parent close $parent or die "close: $!\n"; sleep 1; print STDOUT time%1000, ": polling child\n"; if ( IO::Select->new($child)->can_read(0) ) { ############### CHA +NGE my $line = <$child>; chomp $line; print STDOUT time%1000, ": received <$line>\n"; } else { print STDOUT time%1000, ": no input from child yet\n"; } } else { #child die "cannot fork: $!" unless defined $pid; close $child or die "close: $!\n"; print STDOUT time%1000, ": child started\n"; sleep 5; print {$parent} time%1000, ": child printed\n"; close $parent or die "close: $!\n"; exit; }
Re: eof() blocking
by choroba (Cardinal) on Nov 04, 2020 at 21:02 UTC
    From perldoc -f eof:

    > eof
    Returns 1 if the next read on FILEHANDLE will return end of file or if FILEHANDLE is not open. FILEHANDLE may be an expression whose value gives the real filehandle. (Note that this function actually reads a character and then "ungetc"s it, so isn't useful in an interactive context.)

    A filehandle to which nothing has been printed is like a Schrödinger's cat. You can't tell whether there will be an eof before something writes to it or closes it.

    Update: I like tybalt89's answer, but I guess you wanted to be notified that the parent is waiting for the child. Use IO::Select for that:

    #!/usr/bin/perl use warnings; use strict; use Socket; use IO::Select; my ($child, $parent); socketpair $child, $parent, AF_UNIX, SOCK_STREAM, PF_UNSPEC or die "socketpair: $!"; $child->autoflush(1); $parent->autoflush(1); if (my $pid = fork) { close $parent or die "close: $!\n"; print STDOUT time % 100, ": polling child\n"; my $select = 'IO::Select'->new; $select->add($child); my $buf = ''; COMMUNICATION: while (1) { if ($select->can_read(0)) { sysread $child, $buf, 4096, length $buf or last COMMUNICATION; if ($buf =~ s/(.*)\n//) { print STDOUT time % 100, ": received <$1>\n"; } } else { print STDOUT time % 100, ": waiting for child.\n"; sleep 1; } } print STDOUT time % 100, ": end of input from child\n"; wait; } elsif (defined $pid) { close $child or die "close: $!\n"; print STDOUT time % 100, ": child started\n"; sleep 5; print $parent time % 100, ": child printed\n"; sleep 2; print $parent time % 100, ": child printed again\n"; close $parent or die "close: $!\n"; exit } else { die "cannot fork: $!" }
    map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
      In my simple-minded way, I thought a filehandle was an ID of a buffer which either has characters waiting to be read or not. Apparently it's not that simple. Thanks for illustrating how it can be done with IO::Select.