http://qs321.pair.com?node_id=794885

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

The following code snippet terminates ok:

open FH , "| cat " ; open STDOUT, ">&FH";
but if you add a variable holding a regular expression, like

my $foo = qr(a); open FH , "| cat " ; open STDOUT, ">&FH";
then this code hangs, according to strace while waiting for the forked process to terminate:

... rt_sigaction(SIGQUIT, {SIG_IGN}, {SIG_DFL}, 8) = 0 waitpid(29096, ...
Is this a known perl bug?

Replies are listed 'Best First'.
Re: Variable triggers global destruction hang
by ikegami (Patriarch) on Sep 12, 2009 at 04:21 UTC

    I don't get any difference in behaviour for your code. What Perl version and system?


    Now, I do expect the following difference in behaviour:

    $ perl -e'open my $fh, "cat |" or die $!' <waits forever> $ perl -e'open our $fh, "cat |" or die $!' <returns immediately>

    When all references to a variable cease to exist, it is freed. In the case of this kind of file handle, Perl wait for the child process to end. cat never ends unless told to, so perl can wait for a long time.

    In the first case, all references to $fh cease to exist when execution reaches the end of the file. Thus, when Perl reaches that point of the program, it starts waiting for cat to end.

    In the second case, the file handle survives beyond the end of the file and into global destruction since it exists in the symbol table. The "bug" is that file handles aren't closed during global destruction — Perl let's the system do it — so cat is left running, ignored.

    I suppose you could call it a bug. It's definitely known behaviour. It's easy to work around, though. Just call close($fh); explicitly.

    $ perl -e'open our $fh, "cat |" or die $!; close($fh)' <waits forever>

    On the flip side, you can always force cat to terminate early using kill.

    $ perl -e'my $pid = open my $fh, "cat |" or die $!; kill TERM => $pid' <returns immediately>
      What Perl version and system?
      Just verified: With an old perl-5.8 I get the different behavior with/without the variable, with perl-5.10 it hangs regardless. It's reproducable (on 5.8) with variables that hold regular expressions, not with variables that hold scalars. Really weird, probably an old bug that got fixed in 5.10.
      The "bug" is that file handles aren't closed during global destruction — Perl let's the system do it — so cat is left running, ignored.
      Nice, that explains your examples well, but what is the reason that

      open FH , "| cat " or die ; open STDOUT, ">&FH"or die ;
      hangs while

      open FH , "| cat " or die ;
      doesn't?

        Neither of those hang for me. (5.8.8 on linux)

        Update: I just figured out why it hangs in 5.10+.

        Closing the Perl handle will cause perl to close the system handle and wait for cat to end.

        Without duping: Since the pipe is closed on Perl's side, it gets closed on cat's side and cat exits. perl's wait is short.

        With duping and STDOUT gets closed after FH: Since the pipe is still open on Perl's side because of the dup, it doesn't gets closed on cat's side and cat doesn't exit. perl's wait is "infinite".

        With duping and STDOUT gets closed before FH: It's like there was no duping.

        (The order in which stuff gets freed during global destruction is undefined and/or unpredictable.)

        update: This might apply to the cases in the original post, but not to those in Re^2: Variable triggers global destruction hang - my mistake.

        Given that your build of perl is waiting for the sub-process to finish, as indicated by the strace output you posted, my guess is that the order of cleanup is variable. If the file handles are closed before waiting for the sub-process to finish, then the sub-process exits when it reads EOF from its standard input, and then perl exits. But if perl waits for the sub-process to finish before closing file handles then it waits forever because the sub-process never reads EOF from its standard input.

        Run your program under strace, wait for it to hang, then kill the cat process and see what follows. I expect you will see that after the wait completes (it will complete when cat is killed) perl will go on to close its file handles.

        Explicitly close your file handles and see what happens. I don't know why it is waiting in the first place, but if it waits as part of the close I would close STDOUT first on the assumption that the magic that causes the wait is associated with FH. If you close FH first and it waits, then it should wait forever because STDOUT is still open so cat shouldn't read EOF.