Beefy Boxes and Bandwidth Generously Provided by pair Networks
The stupid question is the question not asked

Safe Pipe Opens and Implicit Loops

by martin (Friar)
on Jul 08, 2010 at 20:04 UTC ( #848755=perlmeditation: print w/ replies, xml ) Need Help??

I have frequently used the pseudo-filenames "-|" and "|-" as suggested in perldoc perlipc to safely open pipes, i.e. in a way that I can control what should happen just after forking and on failure.

With recent perls -- I have checked 5.10.1 and 5.12.1 on Linux and MacOS X --, however, part of that control seems to have been lost. Safe pipes now seem to loop over EAGAIN error events. There is even a warning issued when such events occur:

Can't fork, trying again in 5 seconds at ... line ...

I wonder how perl code is now supposed to break out of such a loop, or how new semantics could slip undocumented into such a basic construct.

(EAGAIN is the error code by which most Unix(-like) systems tell the caller that fork() was temporarily not possible.)

Quoting perlipc, this is where I got the idea that a retry-loop in such a scenario was my own responsibility to set up:

my $sleep_count = 0; do { $pid = open(KID_TO_WRITE, "|-"); unless (defined $pid) { warn "cannot fork: $!"; die "bailing out" if $sleep_count++ > 6; sleep 10; } } until defined $pid; # ...
Moreover, if sleeping and issuing warnings was not what I wanted to happen, I could always omit the loop and bail out right away, like so:
$pid = open(KID_TO_WRITE, "|-"); die "bailing out" if !defined $pid;

Unfortunately, this no longer seems to be the case. Code like that second example now loops and warns indefinitely on its own accord, if the process happens not to be able to fork for some time.

A workaround I have come up with is to hook onto the warning, like this:

$fh = undef; $pid = eval { use warnings FATAL => 'pipe'; open($fh, '|-') }; die 'bailing out' if !defined $pid;
I do not claim this to be backwards compatible with very old perls, however.

Personally, I consider the new Safe Pipe semantics a bug, seeing as '|-' and '-|' had been a conveniently low-level feature before.

The only reason I can think of why implicit retrys might seem preferable is that repeated pipe() system calls would not be wasted during the loop. But optimizations like that should not be bought at the price of changing behaviour of code that actually still seems to be backed by the documentation.

But how long ago has this change taken place? Just before posting this article I found out that perl 5.8.9 seems to have the loop but not the warning, rendering my workaround useless. A perl 5.8 program just hangs at the open statement as long as no new processes are available. Sigh.

It would be possible to emulate the one-shot Safe Pipe Open behaviour using pipe(), fork() and STDIN/STDOUT reassignment explicitly. That would of course no longer be a concise perl idiom unless provided by a module. A cursory CPAN search just now did not bring up anything of the kind. (Wink, wink, wink -- any volunteers?)

Feel free to comment or suggest other solutions.

Comment on Safe Pipe Opens and Implicit Loops
Select or Download Code
Replies are listed 'Best First'.
Re: Safe Pipe Opens and Implicit Loops
by ikegami (Pope) on Jul 08, 2010 at 21:52 UTC

    I agree. It seems to me that retrying should be the job of a module, not that of builtins.

    Mind you, the behaviour is not new. It dates back to at least Perl 3.

    system also has this loop, but it doesn't warn.
    fork does not have this loop.

    Instead of hooking into the warning, you could use alarm.

    Or you could avoid the issue completely by using open3 (since it wraps fork).

    my $pid = open3(*KID_TO_WRITE, '>&STDOUT', '>&STDERR', '-'); ... waitpid($pid, 0);
      Instead of hooking into the warning, you could use alarm.

      No luck with that. At least on MacOS X, pending alarms seem to be postponed while open is doing its retry loop. Maybe the signal is needed for waiting between retries and other SIGALRM handlers are deactivated hence. I see them fire in both parent and child once fork is successful, but not during the retry loop, which does not help at all.

      To summarize, open3 seems to be the way to go, and I stand corrected in that the behaviour is not quite new.

        try enabling unsafe signals

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlmeditation [id://848755]
Approved by Corion
Front-paged by ww
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others having an uproarious good time at the Monastery: (6)
As of 2016-02-13 06:01 GMT
Find Nodes?
    Voting Booth?

    How many photographs, souvenirs, artworks, trophies or other decorative objects are displayed in your home?

    Results (419 votes), past polls