in reply to Variable triggers global destruction hang

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>

Replies are listed 'Best First'.
Re^2: Variable triggers global destruction hang
by saintmike (Vicar) on Sep 12, 2009 at 06:26 UTC
    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.)

        But the OP has duplicated the file handle in both cases.

        I have tried with 5.8.8 (redhat build) and 5.10.0 (default build) on Linux. Neither waits on the sub-process - neither hangs. But the OP says 5.10 hangs with either form. If it is hanging waiting for the sub-process, then something very different is happening.

        So, question for the OP: who's build and on what platform are you seeing this?

      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.