TheAschr
TheAschr

Reputation: 973

Unable to read pipe

I don't know if I accidentally deleted or put in a typo somewhere but all of the sudden some of my code stopped working. For some reason no lines are ever read from $in.

use Win32::Job;
use IO::Handle; 

STDOUT->autoflush;

pipe my $in, my $out;

my $job = Win32::Job->new;

sub flush_pipe{
    while (defined(my $line = <$in>)) {
       chomp($line);
       print($line);

    }
}
my $pid = $job->spawn("cmd", "cmd /C \"ipconfig\"",
    {
        stdout=>$out
    }
);
flush_pipe();

Edit: Through trial and error I eventually found out I have to close the $out filehandle before flushing the pipe.

Upvotes: 1

Views: 206

Answers (1)

zdim
zdim

Reputation: 66883

A pipe is unidirectional. Each of the processes it connects can either read or write.

After pipe you have two filehandles, and both the parent and the child see them both. If the child is going to write and the parent to read, as in your code, then the child must first close the handle it won't use ($in) and the parent must close its unused one, $out. Otherwise you'll have deadlocks.

The spawn from the module starts a child process (or, rather, a Windows approximation of it) and redirects its STDOUT to the writing end of the pipe, $out.

Some very basic code that should cover this

use strict;
use warnings;
use feature 'say';

pipe my $in, my $out;

my $pid = fork // die "Can't fork: $!";

if ($pid == 0) { # child
    close $in;
    print $out "hi ";         # can't read this yet (no newline) ...
    sleep 1;
    say   $out "from child";  # now the other end can read it
    close $out;
    exit;
}

# parent
close $out;

say while <$in>;
close $in;

wait;

When you want prints to become available to the reader right away (up to buffering outside your code) send a newline. Close unused ends of the pipe in each process before doing anything else.

I can't write code on Windows now, but in your code parent must close $out (after spawn).

The term "flush" here can relate to the code in the writer or to Perl's clearing of IO buffers; the code in your flush_pipe() merely reads the pipe. So I'd change that name, to read_pipe or such.

Upvotes: 5

Related Questions