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

Just ran across this in a program I'm working on. I've got some code swiped mostly verbatim from the Cookbook (sub new_tmpfile_name() ) that works great. I added some code to print a usage statement though and discovered that things don't work like I think the documentation says they should.

Run it without any flags and you get a "Can't unlink file" message because the END block doesn't have a file name to unlink. So the END block is being queued for execution at compile time like the documentation says.

Run it with the flag and the file name is known in the END block and the file gets unlinked. Obviously the END block was queued at run time, which is not what the documentation would lead me to believe should happen.

I'd even be okay with it if the END blocks were queued up both when compiling and when calling the function. If that were the case though, I should see the "Can't unlink" error even when passing the -r flag.

I can stop the error message by wrapping the code in the END block with an if ($name){} block, but does anybody understand what's really going on here? I suspect it's some DWIM magic, but it's causing me more confusion than I'd like.

Thanks,
--Larry

#!/usr/bin/perl die "Usage: $0 -r\n" unless ( grep /^-r$/, @ARGV); my $testingfile = new_tmpfile_name(); print "Pretending to do something with $testingfile...\n"; exit 0; #-------------------------------------------------------------------- # Make up a temporary file name that is not already in use # elsewhere and arrange for it to be deleted automatically when # the program exits. # # This will get you in trouble if you ever use it with mod_perl. # BTW: 90% of this is stolen from the Ram recipe number 7.5 #-------------------------------------------------------------------- sub new_tmpfile_name { use IO::File; use POSIX qw(tmpnam); my ($name, $fh); # try new temp filenames 'til we get one that doesn't exist do { $name = tmpnam() } until $fh = IO::File->new($name, O_RDWR|O_CREAT|O_EXCL); # install atexit-style handler so that when we exit or die # we automatically delete this temporary file END { unlink($name) or die "Couldn't unlink $name : $!" } return $name; }

Replies are listed 'Best First'.
Re (tilly) 1: END blocks created at run time?
by tilly (Archbishop) on May 08, 2001 at 04:07 UTC
    I would recommend using File::Temp for the actual problem.

    For the general construct though, END blocks are the wrong answer. They fire when Perl stops running, not before you exit your subroutine. Instead you probably wanted to use ReleaseAction...

    UPDATE
    bbfu is right about when the delete should happen. But you could still use ReleaseAction as follows:

    $$$fh = ReleaseAction->new( sub {unlink $file or die "Cannot unlink $file: $!"} );
    Now whenever $fh goes away, the file is deleted. (There may be an order of action issue though.)-:

      Actually, tilly, I think that lemmett did want the END blocks to fire when Perl exited, not the subroutine. It would be kind of silly to create a temp file and then proceed to delete it before passing it back to the user. :-)

      But I whole-heartedly agree with your suggestion to use File::Temp.

      Update (for tilly's update): I like the idea of using a ReleaseAction for unlinking the file as long as the order of execution is OK. That's certainly an interesting way of binding a ReleaseAction to the filehandle (assigning the action reference to the scalar part of the anonymous symbol, right?). I hadn't thought about sticking extra info into the filehandle like that. :-)

      bbfu
      Seasons don't fear The Reaper.
      Nor do the wind, the sun, and the rain.
      We can be like they are.

(bbfu) Re: END blocks created at run time?
by bbfu (Curate) on May 08, 2001 at 04:12 UTC

    It's not "queueing" the END block at run time. The problem is that you're dieing before assigning a value to your variable. So, in effect, you're trying to unlink undef.

    You're thinking that the END block shouldn't be called if the subroutine is not called because you've placed the definition inside the subroutine body... That's incorrect. Named subroutines (of which BEGIN and END are just special breeds) are non-lexical. That means they all belong to the package they're declared in, not just the block. The only thing you're accomplishing by declaring your END routine inside the body of new_tmpfile_name is that you're creating a named closure (ie, END keeps a reference to $name, thus keeping it alive longer than it otherwise would be).

    A corollary to this is the fact that if you were to call new_tmpfile_name multiple times, END would still only be called once and only the first temp file unlinked. I suspect this is not what you mean to happen.

    A possible solution to do what you seem to be attempting would be to do a string eval. This has an unfortunate side-effect of being a little slow. :-( But it would definately work. :-) Anyway, I hope this bit of explaination helps. Have fun coding!

    bbfu
    Seasons don't fear The Reaper.
    Nor do the wind, the sun, and the rain.
    We can be like they are.

(tye)Re: END blocks created at run time?
by tye (Sage) on May 08, 2001 at 07:01 UTC
    { my @toDelete; END { for( @toDelete ) { unlink $_ or warn "Can't delete $_: $!\n"; } } sub new_tmpfile_name { # ... push @toDelete, $name; return $name; } }

    This handles both dieing before any files have been create and dieing after more than one file has been created. Just another way to do it.

            - tye (but my friends call me "Tye")
      You can generalize this:
      BEGIN { my @coderef; END { $_->() for @coderef } sub atEND { push @coderef, @_ } } ... sub new_tempfile_name { ... atEND(sub { unlink $name }); return $name; }
      Now you can add other things dynamically as well, using one mechanism, just by passing a coderef (usually a closure).

      -- Randal L. Schwartz, Perl hacker

Re: END blocks created at run time?
by lemmett (Sexton) on May 08, 2001 at 23:53 UTC
    Okay, here's where the self-flagellation part of this whole monastery theme comes in.

    bbfu, you gave me the piece I needed to see the light with:

    A corollary to this is the fact that if you were to call new_tmpfile_name multiple times, END would still only be called once and only the first temp file unlinked. I suspect this is not what you mean to happen.

    There's no contradiction between how I think END block queuing works once I realized that the program isn't working correctly. There are a lot of temp files not getting deleted. Why did I think it was working? All of my testing (using just a single temp file) worked great. Makes me think of this joke.

    Not sure why I didn't trigger to the END-block-in-a-function weirdness when I originally wrote it. Hopefully it was just late!!

    tilly thanks for the pointers about File::Temp and ReleaseAction. File::Temp I knew about and purposefully chose not to use. For this application I'm not in danger of a race condition nor do I need the portability or additional functionality. Because of the specific systems this is deployed on, getting the module deployed on all of them is a lot more of a hassle than it should be. ReleaseAction looks very cool and I'll be pulling it down and playing with it soon.

    bbfu thanks for triggering the flash of insight. I thought about the eval approach but decided against it.

    tye I implemented something almost identical to this shortly after I ran into the problem. merlyn's got an interesting take on it that I'll keep in mind, but I don't need the general case this time around.

    buckaduck The conflict I thought I saw with the documentation was with a chart at the end of chapter 18 in the Camel v3. Even though I knew END blocks should all get queued at compile time, when I thought the code was working it looked like they were getting queued at both compile and run time.

    Again, thanks for the help. I thought this might be another case of perl acting a little too smart. Kind of like when Dominus pointed out that these two lines do the same thing:

    print "\L\uTeStInG\E.\n"; print "\u\LTeStInG\E.\n";
Re: END blocks created at run time?
by buckaduck (Chaplain) on May 08, 2001 at 19:36 UTC
    What part of the documentation leads you to believe that END {} executes at compile-time? From what I read in perlman:perlmod,
    An END subroutine is executed as late as possible, 
    that is, when the interpreter is being exited, 
    even if it is exiting as a result of a die() function. 
    

    In your first test case (where you don't specify a file name), you seem to think that END {} was executed at compile-time. In fact, it's executing at the end of run-time, after you call the die() command. It seems straightforward to me, or am I confused about what you mean?

    Update:Ah, thanks as always, tye.

    buckaduck

      END blocks are queued at compile time. The desire was to queue an END block at run time.

              - tye (but my friends call me "Tye")