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

Fellow Monks,

my Google-fu isn’t being too good to me today, so I might be asking something that’s been asked a million times before. Sorry about that.

I have an object, and for various reasons, I’d like to have a DESTROY sub sometimes, but sometimes not. This destructor kills some processes related to this object as a last-minute clean up, but in some situations this is something that wreaks havoc and hence the conditional. So I thought of passing another param to new() and if it’s set, I'd like to bail out from the DESTROY call (return if $self->{no_destroy}). But it turns out that this variable is no longer set by the time we arrive to the DESTROY sub.

Is there anything I could do?


UPDATE: It seems it was a typical brown paper bag-quality 8th layer error. See the reduced code example in response to Corion’s reply, it works just fine.

Replies are listed 'Best First'.
Re: Conditional DESTROY
by BrowserUk (Patriarch) on Jun 10, 2013 at 09:45 UTC

    You could try storing the flag outside of the object itself:

    package whatever; my %customDestroy; sub new { my( $class, $flag, ... ) = @_; my $self = {}; ... $customDestroy{ $self } = 1 if $flag; return bless $self, $slass; } ... sub DESTROY { my $self = shift; if( $customDestroy{ $self } ) { ## special behaviour } else { ## normal behaviour } }

    I think package lexicals should persist long enough to outlive instances, though I haven't tested the hypothesis.


    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

      See update and Corion’s thread. It was unfortunately a really stupid PEBKAC involving messing around with //= and defining the $self->{no_DESTROY} to a non-undef value beforehand — thus never actually setting it to 1 :( I must have been quite tired on Friday.

Re: Conditional DESTROY
by Corion (Patriarch) on Jun 10, 2013 at 09:29 UTC

    This surprises me. $self and the hash referenced by it should always be valid, even when global destruction sets in. What might have gone already are objects referenced by %$self.

    At least for plain hash-based objects, I don't know of failure cases like you describe. Can you please post a short example that (sometimes) exhibits the problem you describe? I understand that it is hard to reduce a large program where global destruction happens to a small example that condenses the problem to a few lines.

    As a workaround, you can prevent your objects falling prey to global destructions by releasing them earlier, through strategically undeffing the referencing variables:

    my $master= My::Master::Object->new(); ... undef $master; # release object tree before global destruction sets in # end of source code

    If you already try to be clever and use Scalar::Util::weaken, maybe you are accessing a weakened reference somewhere, which has rightfully lost its grip onto the object data already?

      Reduced the code, something is truly messing with me, because this thing runs perfectly:

      package Foo; sub new { my ($class, $params) = @_; my $self = {}; $self->{no_DESTROY} = $params->{no_DESTROY} // 0; bless($self, $class); return $self; } sub DESTROY { my $self = shift; print "in DESTROY, no_DESTROY is $self->{no_DESTROY}\n"; return if $self->{no_DESTROY}; print "DESTROY body is running!\n"; } package main; my $foo = Foo->new({ no_DESTROY => 1 });

      Argh. I messed with this most of Friday and it wouldn’t budge, now the mock-up code Just Works (as actually expected, too).

        To track this down better, maybe you can dump the object(s) in question at strategic places of the lifetime of your program. Maybe you get lucky and find that no_DESTROY gets reset/lost somewhere or a place where another object holds on too long to the object in question.

        Note that closures are notorious for holding on surprisingly long to objects:

        sub make_frobnitzer { my( $self )= @_; $self->{on_error}= sub { $self->error_on_frobnitz( @_ ); } }

        In the above case, $self will live until global destruction, because the on_error subroutine closes over $self.

Re: Conditional DESTROY
by syphilis (Archbishop) on Jun 10, 2013 at 09:45 UTC
    Is there anything I could do?

    Could you provide us with a simple script that does little (if anything) other than demonstrate the behaviour you describe ?

    Cheers,
    Rob

      See update and Corion’s thread.