Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic
 
PerlMonks  

Re^2: is there a way to ensure some code is the last thing that is run?

by morgon (Priest)
on Feb 03, 2017 at 02:16 UTC ( [id://1180914]=note: print w/replies, xml ) Need Help??


in reply to Re: is there a way to ensure some code is the last thing that is run?
in thread is there a way to ensure some code is the last thing that is run?

As a trivial example:
package Blubb; + sub new { my $this = bless {}; $this->{circular} = $this; } sub DESTROY { print "DESTROY\n"; } package MAIN; my $b = Blubb->new; END { print "END\n"; }
produces:
END DESTROY
Now assume you have some code that someone else has written and where destructors run as above during gobal destruction and one of them (you are too lazy to figure out which) is doing something nasty.

Would it be (as a hack of course) then possible to graft something on to ensure that the nasty thing is undone as the last thing that happens?

My understanding is that during global destruction all bets are off and so I guess it is not possible, but then what do I know...

Replies are listed 'Best First'.
Re^3: is there a way to ensure some code is the last thing that is run?
by BrowserUk (Patriarch) on Feb 03, 2017 at 09:08 UTC

    If all you are after doing it avoiding global cleanup, you could do:

    package Blubb; sub new { my $this = bless {}; $this->{circular} = $this; } sub DESTROY { print "DESTROY\n"; } package MAIN; use POSIX (); my $b = Blubb->new; POSIX::_exit(0);

    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". The enemy of (IT) success is complexity.
    In the absence of evidence, opinion is indistinguishable from prejudice.
      Good idea, many thanks.

      POSIX::_exit may indeed be all I need for my particular hack.

Re^3: is there a way to ensure some code is the last thing that is run?
by haukex (Archbishop) on Feb 03, 2017 at 08:45 UTC

    Hi morgon,

    Now assume you have some code that someone else has written and where destructors run as above during gobal destruction and one of them (you are too lazy to figure out which) is doing something nasty. Would it be (as a hack of course) then possible to graft something on to ensure that the nasty thing is undone as the last thing that happens?

    From my somewhat limited experience with it, fiddling with stuff during destruction is fraught with peril. I would argue that if someone else's destructor is doing something so complex/nasty in its DESTROY method that you have a need to undo what is being done there, it's that code that is to blame and that needs to be addressed.

    What is this nasty thing you're trying to undo?

    Regards,
    -- Hauke D

Re^3: is there a way to ensure some code is the last thing that is run? (updated garbage collection)
by LanX (Saint) on Feb 03, 2017 at 09:17 UTC
    Destruction happens at the end of scope not file.

    Try to put curlies around the code excluding the END.

    In other words make sure the variables are in another scope than the END block.

    UPDATE

    Tested, doesn't work with your code!

    BUT the problem is your circular reference.

    It means that a normal destruction can't happen because the ref-count keeps up.

    We are talking here about the garbage collection at the very end of a Perl script..

    UPDATE
    (on exit Perl 5 does a mark and sweep to reclaim circular references.)

    Cheers Rolf
    (addicted to the Perl Programming Language and ☆☆☆☆ :)
    Je suis Charlie!

      Hi LanX,

      Try to put curlies around the code excluding the END.

      Unfortunately, that doesn't seem to work in this case. I assume that because of the circular reference, the object really is the very last thing to be destroyed, even after the END block.

      Regards,
      -- Hauke D

        Yes I just updated my post.

        here the demo:

        package Blubb; + sub new { my $this = bless {}; # $this->{circular} = $this; } sub DESTROY { warn "DESTROY\n"; } package MAIN; #{ my $b = Blubb->new; #} END { warn "END\n"; }

        DESTROY END

        What's surprising me is that it already works without curlies. I expected the variable to be accessible in END, like in BEGIN.


        The only chance I see is to have a second dummy variable which is guaranteed to be destroyed later and to put the code into it's destructor.

        Hauke, I'm sure you want to try this out! ;-)

        Cheers Rolf
        (addicted to the Perl Programming Language and ☆☆☆☆ :)
        Je suis Charlie!

Re^3: is there a way to ensure some code is the last thing that is run?
by ikegami (Patriarch) on Feb 03, 2017 at 18:21 UTC
    Or something that wouldn't be considered buggy:
    package Logger; our $default_logger = __PACKAGE__->new(); sub new { my $this = bless {}; } sub DESTROY { print "DESTROY\n"; } package MAIN; END { print "END\n"; }
    or
    package Logger; my $default_logger = __PACKAGE__->new(); sub new { my $this = bless {}; } sub log { $default_logger->foo(); } sub DESTROY { print "DESTROY\n"; } package MAIN; END { print "END\n"; }

    In both cases, a package variable ($Logger::default_logger in the first snippet, &Logger::log in the second snippet) keep the object alive until global destruction (where package variables are destroyed).

Re^3: is there a way to ensure some code is the last thing that is run? (monkey patching)
by LanX (Saint) on Feb 03, 2017 at 11:10 UTC
    > Would it be (as a hack of course) then possible to graft something on to ensure that the nasty thing is undone as the last thing that happens?

    Maybe by monkey patching DESTROY?

    use strict; use warnings; package Blubb; sub new { my $this = bless {a=>42}; $this->{circular} = $this; } sub DESTROY { warn "DESTROY"; } package MAIN; my $b = Blubb->new; { no warnings 'redefine'; my $orig= \&Blubb::DESTROY; *Blubb::DESTROY = sub { $orig->(@_); warn "END"; }; }

    DESTROY at c:/tmp/pm/destruct_end.pl line 12 during global destruction +. END at c:/tmp/pm/destruct_end.pl line 27 during global destruction.

    Cheers Rolf
    (addicted to the Perl Programming Language and ☆☆☆☆ :)
    Je suis Charlie!

      The following will demonstrate an approach to define a &SUPEREND::DESTROY which is run after all other global DESTROY()s happend.

      use strict; use warnings; package Blubb; sub new { my $this = bless {a=>42}; $this->{circular} = $this; } sub DESTROY { warn "DESTROY ". __PACKAGE__; } package SUPEREND; no warnings 'redefine'; my $orig_destroy= \&Blubb::DESTROY; my $persist = bless { count=>0}; *Blubb::DESTROY= sub { $orig_destroy->(@_); warn "Mini END"; $persist->{count}++; }; sub DESTROY { warn "DESTROY after $_[0]->{count} destroys ". __PACKAGE__; } package MAIN; my $b = Blubb->new; my $c = Blubb->new; my $d = Blubb->new;

      DESTROY Blubb at c:/tmp/pm/destruct_end.pl line 12 during global destr +uction. Mini END at c:/tmp/pm/destruct_end.pl line 25 during global destructio +n. DESTROY Blubb at c:/tmp/pm/destruct_end.pl line 12 during global destr +uction. Mini END at c:/tmp/pm/destruct_end.pl line 25 during global destructio +n. DESTROY Blubb at c:/tmp/pm/destruct_end.pl line 12 during global destr +uction. Mini END at c:/tmp/pm/destruct_end.pl line 25 during global destructio +n. DESTROY after 3 destroys SUPEREND at c:/tmp/pm/destruct_end.pl line 33 + during global destruction.

      Please note, that this can be extended to be a generic solution, because SUPEREND could parse all packages available and monkeypatch all DESTROYs there (if existent)

      Since $persist is referenced it's DESTROY will be run last.

      For syntactic sugar one could also make SUPEREND export a function SUPEREND(&) accepting a codeblock which is run by the DESTROY of $persist.

      Cheers Rolf
      (addicted to the Perl Programming Language and ☆☆☆☆ :)
      Je suis Charlie!

        I think in the general case you cannot have a SUPEREND or something like that.

        The reason is that this SUPEREND could again create an object and the destructor of that would again run later.

        Lastestdestructor not superend

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://1180914]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others pondering the Monastery: (4)
As of 2024-04-19 20:51 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found