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

Hi all; first time here but I've been hacking Perl for many years (and UNIX for even longer). I have a super-bizarre problem that I hope some Perl or UNIX guru can advise me on.

I wrote some code back in 2007 that wants to find out the maximum # of bytes I can write to a pipe without it blocking (I want to know the pipe buffer size). So, I have this:

my ($rd, $wd, $sz); pipe($rd, $wd) or die "pipe(print): $!\n"; $sz = fpathconf($wd, _PC_PIPE_BUF) or die "fpathconf(pipe): $!\n";

Now, this has worked fine ever since. I'm running it on Red Hat EL 4 (Perl 5.8.5 32bit) and Red Hat EL 5 (Perl 5.8.8 64bit). Lately, for reasons I can't figure out, the second die() above is firing due to fpathconf failure, with an error code of "Bad file descriptor". This doesn't happen every time, and it's happening across different systems at different times, etc. Nothing in the above code has changed. I'm not aware of anything systematic that has changed on any of these systems but I don't have direct control over them.

I'm really stumped. I don't understand how the fpathconf() can fail with "Bad file descriptor", after the pipe() just succeeded in the previous line (the pipe() never fails that I've seen). I added code to print fileno($wd) and it was just "5" (nothing huge like I'm running out of FDs).

To try to harden the code I modified it so that if the fpathconf() fails on the write FD then I try it on the read FD. If that fails I choose a small value (512--on Linux this value is actually 4096) and continue rather than dying. I left some warning messages in for now so I could see if the problem is still happening. I've seen the fpathconf() on the write FD failing, then the fpathconf() on the read FD succeeds! I don't know (since the problem is intermittent and I have no way to reliably reproduce it) if this really fixes the problem or if I was just lucky. And, I have no idea WHY this would be the case

I'm hoping someone who knows more about the guts of this can give me some thoughts on how to handle it.

Replies are listed 'Best First'.
Re: Why does fpathconf give "bad file descriptor"?
by ikegami (Patriarch) on Mar 31, 2010 at 15:23 UTC
    Could it be returning zero? The proper usage is
    defined( $sz = fpathconf($wd, _PC_PIPE_BUF) ) or die "fpathconf(pipe): $!\n";

    Update: Not quite. As ReturnOfThelonious points out, the function appears to expect a file number. The proper usage is actually

    defined( $sz = fpathconf(fileno($wd), _PC_PIPE_BUF) ) or die "fpathconf(pipe): $!\n";

      I did realize that it might be returning 0 and modified my code to check for defined values but that didn't make any difference... plus it's really not possible for a pipe to have a buffer size of 0 so that's just not a valid result.

      As stated, I need to use fileno(). So simple... in hindsight

      Thanks all!

        it's really not possible for a pipe to have a buffer size of 0

        fpathconf says it returns "the maximum number of bytes which will be written atomically to a pipe."

        I took that to mean it returns the available buffer size. The documentation isn't clear, but I'm favouring your interpretation.

        My local man page simply says "returns the size of the pipe buffer", confirming your interpretation.

Re: Why does fpathconf give "bad file descriptor"?
by ReturnOfThelonious (Beadle) on Mar 31, 2010 at 17:14 UTC
    I think you should:
    $sz = fpathconf(fileno($wd), _PC_PIPE_BUF) or die "fpathconf(pipe): $!\n";
    From what I see in POSIX.xs, the conversion of a Perl file handle to a file descriptor is not done for you.

      Aha! That makes perfect sense now that you mention it. So strange that it's worked great all these years... what in the world is actually happening under the covers with that glob such that it works? It must be "usually" resolving to FD 0 or something like that.

        Don't you use warnings?
        $ perl -MPOSIX -wle'print fpathconf(*STDOUT, _PC_PIPE_BUF)' Argument "*main::STDOUT" isn't numeric in subroutine entry at -e line +1. 4096 $ perl -MPOSIX -wle'print fpathconf(*FOO, _PC_PIPE_BUF)' Name "main::FOO" used only once: possible typo at -e line 1. Argument "*main::FOO" isn't numeric in subroutine entry at -e line 1. 4096

        And yes, they are being interpreted as zero.

        $ perl -wle'print 0+*STDOUT' Argument "*main::STDOUT" isn't numeric in addition (+) at -e line 1. 0 $ perl -wle'print 0+*FOO' Name "main::FOO" used only once: possible typo at -e line 1. Argument "*main::FOO" isn't numeric in addition (+) at -e line 1. 0