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

Hi all,

nothing mission critical, just something I don't fully understand yet :)   More precisely, the can_write() method of IO::Select doesn't seem to test what I think its name would suggest.

While playing around with How and When to read STDOUT after using open3() funciton? I encountered a problem when trying to write to the filehandle that open3() connects to STDIN of the command being run. My initial code (simplified to the minimum) was as follows:

#!/usr/bin/perl use strict; use warnings; use IPC::Open3; use IO::Select; my @cmd = qw'echo bar'; # writing to 'echo' fails #my @cmd = qw'cat'; # ... while 'cat' works open3(my $wh, my $rh, undef, @cmd); # tiny delay, so the command gets enough time to close STDIN # (prevent potential race condition, in this demo script) select undef,undef,undef, 0.1; my $msg = "foo"; #----- print $wh "$msg\n"; close $wh; #----- my $data = ""; if (IO::Select->new($rh)->can_read()) { print "reading from $rh ('@cmd |')\n"; while (sysread($rh, my $buffer, 1000)) { $data .= $buffer; } } print "data: $data\n";

Problem with this code is that it simply terminates before it starts doing anything useful. Apparently, the issue is that the command being run via open3() is closing STDIN, so an attempt to write to it causes a SIGPIPE, which in turn leads to the script being killed by the default signal handler... Or some such.   Ok, silly thing to do... to write to a program that doesn't take input, but anyway.

So, I changed the section between the #----- to read:

#----- # handle SIGPIPE ourselves, so we don't get killed $SIG{PIPE} = sub { warn "BROKEN PIPE\n" }; if (my ($w) = IO::Select->new($wh)->can_write()) { if (print $w "$msg\n") { print "wrote '$msg' to $w ('| @cmd')\n"; close $w; } else { warn "print to $w FAILED\n"; # we'll have gotten a SIGPIPE, too } } else { warn "$wh not ready for writing\n"; } #-----

I would've thought that carefully testing with can_write() would avoid running into the situation that's causing the SIGPIPE... However, this doesn't seem to be the case. With the command being echo I still get

BROKEN PIPE print to GLOB(0x6ff5b0) FAILED reading from GLOB(0x6ff360) ('echo bar |') data: bar

while with cat things work as expected (as it doesn't close STDIN):

wrote 'foo' to GLOB(0x63c160) ('| cat') reading from GLOB(0x6ff5a0) ('cat |') data: foo

With echo I would have expected to see

GLOB(0x6ff5b0) not ready for writing reading from GLOB(0x6ff360) ('echo bar |') data: bar

Can someone explain why this doesn't work as I first thought it should?

Hm, on second thought, I figure it has to do with the writability referring to the local side of the pipe only, rather than the entire pipe including the status of the remote side...(?) Which brings up the question: is there a 'proper' way to test if the pipe can be written to (other than waiting for the SIGPIPE to happen, and then handling it)?   Thanks.

Replies are listed 'Best First'.
Re: Pipes and IO::Select's method can_write()
by ikegami (Patriarch) on Feb 02, 2007 at 17:58 UTC

    You shouldn't use print with select. Had you used sysrwite, I bet it would have returned "successfully wrote 0 bytes" (defined and == 0). In other words, the child had ended. (Well, technically, the child has closed its STDIN, but it did that when it ended.)

    Try

    my $msg = "msg\n"; foreach my $w (IO::Select->new($wh)->can_write()) { my $rv = syswrite($w, $msg, length($msg)); if (not defined $rv) { die("Unable to send data to child\n"); } if (not $rv) { warn("Child is no longer accepting input\n"); $w_sel->remove($w); next; } if ($rv != length($msg)) { warn("Pipe is full. We'd normally call select again\n"); next; } print "Successfully sent message to the child\n"; }

    Don't forget to reap your children with waitpid.

      Thanks ikegami.   Seems plausible... (and I first went "you could've known, Almut, you were already using sysread for reading..." to myself).

      But when I actually tried it, even with the code you suggested, I'm still getting a SIGPIPE (which, when uncaught, causes the script to be killed). More precisely, what I get now is

      BROKEN PIPE Unable to send data to child

      (BTW, this is Perl 5.8.8 on Linux - in case it matters)

        You have two mechanisms to detect the child ending. You can either ignore SIGPIPE signals and wait for syswrite to return 0, or you can listen for SIGPIPE signals. It's your choice. (Well, I suppose you could do both.)

        As for print vs syswrite, the problem with print is that it will block until all the data is sent. If that's not desireable, the data needs be appended to a buf and sent when can_write says you can. At that point, syswrite will only send as much as it can without blocking. (Remove the chars that were sent from the buffer, and wait on can_write again).

        If you do use print, make sure the file handle is flushed or auto-flushed.