I have a Perl script that uses an external tool (cleartool) to gather information about a list of files. I want to use IPC to avoid spawning a new process for each file:
use IPC::Open2;
my ($cin, $cout);
my $child = open2($cout, $cin, 'cleartool');
Commands that return single-lines work well. e.g.
print $cin "describe -short $file\n";
my $description = <$cout>;
Commands that return multiple lines have me at a dead end for how to consume the entire response without getting hung up by a blocking read:
print $cin "lshistory $file\n";
# read and process $cout...
I've tried to set the filehandle for non-blocking reads via fcntl
:
use Fcntl;
my $flags = '';
fcntl($cout, F_GETFL, $flags);
$flags |= O_NONBLOCK;
fcntl($cout, F_SETFL, $flags);
but Fcntl dies with the message "Your vendor has not defined Fcntl macro F_GETFL."
I've tried using IO::Handle to set $cout->blocking(0)
but that fails (it returns undef
and sets $!
to "Unknown error").
I've tried to use select
to determine if there's data available before attempting to read:
my $rfd = '';
vec($rfd, fileno($cout), 1) = 1;
while (select($rfd, undef, undef, 0) >= 0) {
my $n = read($cout, $buffer, 1024);
print "Read $n bytes\n";
# do something with $buffer...
}
but that hangs without ever reading anything. Does anyone know how to make this work (on Windows)?
By default, read() waits until at least one byte is available to return to the application; this default is called “blocking” mode. Alternatively, individual file descriptors can be switched to “non-blocking” mode, which means that a read() on a slow file will return immediately, even if no bytes are available.
Non-blocking I/O avoids the client being blocked while waiting for a request to be accepted by the transport layer during one-way messaging for connection-oriented protocols. For connection-oriented protocols, there is a limit to the amount of data that can be put in a network protocol queue.
Read/Write Are Blocking - when a process reads from a named pipe that has no data in it, the reading process is blocked. It does not receive an end of file (EOF) value, like when reading from a file.
select
only works on sockets in Windows. It looks like IPC::OpenX uses normal filehandles, so you won't be able to use select
with the handles it creates.
If you don't need the timeout/detection of activity that select provides, you can set the handles to be non-blocking and just read or write as per normal.
If you need more nuanced control, IPC::Run may work well for you.
You could also look at creating a socketpair
and use those handles with your child processes. Newer perls (5.8 and up) support socketpair
emulation on Windows using TCP sockets.
If you try to clone STDOUT
and STDERR
for a program that runs without a console (ie it is started with wperl, instead of perl), you won't be able to get data through STDIO.
In practice this has been a huge pain for me on several projects. What I found worked best was to write the child process to connect to the parent server via TCP. If you don't control the child processes, look at IPC::Run
or socketpair
.
Another kludge is to use sysread
with a large or unlikely buffer size.
print $cin "$command_for_cleartool\n";
my ($description, $maxlen, $buffer) = ("", 65336);
while (my $n = sysread $cout, $buffer, $maxlen) {
$description .= $buffer;
last if $n < $maxlen;
}
... do something with $description ...
sysread
will hang if there are exactly 0 bytes of input waiting to be read. So the code above will hang if cleartool produces exactly some multiple of 65336 bytes. If you know a good upper bound on the size of output from the program, you can use that value for $maxlen
above. Otherwise, you could pick a large and unlikely number and pray ...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With