Re: piping from external program not working..



On Fri, Jun 04, 2004 at 11:31:27 -0400, muppet wrote:

On Friday, June 4, 2004, at 04:14 AM, Syed Imran wrote:

It shows only the first line of output produced by the external 
program.  I am having difficulty locating the problem. I've added the 
code to this mail.

your test program exits almost immediately after printing a couple of 
lines of output.  the watch handler reads one line of input at a time.

fork child.
install handler.
child prints three lines of output and exits.
your io watch fires with the condition [ 'in', 'hup' ]
you read and process one line of input.
you close the handle.


this is something of a race condition.  i could make your code print 
all the lines that come back by adding some pauses after each line of 
input.  in a Real Program, you wouldn't be able to do that, and you'd 
want to read everything that comes back.

It's not a race-condition. It's just plain old bug. You must not exit
until read returned 0, meaning no more characters to read.

there are two ways to slurp in all data in Perl (of which *i* know -- 
there may be others.  all you mongers listening, please feel free to 
correct me):

1) set $/ to undef, to read till end of file.  this is undesirable, 
because your watch handler will hang (since the stream is not yet 
finished).

2) find out how many bytes are actually waiting, and read exactly that 
many.  this requires the use of ioctl and sysread, and is immediately 
non-portable.  since you're using fdisk, you probably don't care about 
that.

3) Force the file to non-blocking mode and use 1. Fcntl should be
   sufficiently portable.

4) Use sysread. Sysread does not clear the 'in' condition, so your
   callback will be called again. Just you must not exist on 'hup' until
   sysread has really returned 0. Without nonblock mode, you must only
   call sysread once per invocation.

5) Use 3 with 4, just to stay on the safe side.


i got option 2 to work by replacing these lines:

              my $data = <$fh>;

with these:

              #require "sys/ioctl.ph";
              # my perl headers appear to be broken, so here it is, 
              hardcoded.
              sub FIONREAD { 0x541B }
              my $count;
              my $ret = ioctl $fh, &FIONREAD, $count;
              $count = unpack "i", $count;
              my $data;
              sysread $fh, $data, $count;


this no longer works by lines, but that's ok, because the data on the 
input handle may include incomplete lines, anyway.

Unfortunately perlio is not ready for non-blocking descriptors. However,
I can imagine IO layer supporting line oriented io on non-blocking
descriptors, including poll/select support etc.

I really wish perl IO descriptors had a 'buffer' method. It would return
the internal buffer as a (read-only) string. Then we could have Glib::IO
watchers properly generating 'in' event when the perlio descriptor (not
the underlying file) have some data to read!

-------------------------------------------------------------------------------
                                                 Jan 'Bulb' Hudec <bulb ucw cz>

Attachment: signature.asc
Description: Digital signature



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]