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

Hi morgon,

Based on your example code, would it be correct to assume that the problem is happening because of circular references? If so, then tracking those down might be a good start, it should lead you to the culprit code.

I looked into tracking circular references, unfortunately it seems that Devel::LeakTrace and Devel::LeakTrace::Fast don't compile on modern versions of Perl/glib, Devel::Leak seems a bit too low-level, and Devel::Cycle requires you to specify which variables to check for cycles, which can be difficult if you have lots of objects being created. So far, the "best" thing I have found seems to be Devel::Leak::Object, which can override bless.

Also, I fiddled around with Devel::Gladiator and has_circular_ref from Data::Structure::Util, which seems to support more types of data than Devel::Cycle. Note that it appears to be important to limit the inspection to those things that we're actually interested in, in this case objects - just blindly using it on everything that walk_arena() returns leads to errors, and I even got some segfaults at one point. But the following seems to work, at least on your example code:

use Devel::Gladiator qw/walk_arena/; use Data::Structure::Util qw/has_circular_ref/; use Scalar::Util qw/blessed/; sub find_all_cycles { my $all = walk_arena(); for my $sv (@$all) { # limit search to objects next unless blessed($sv); warn "Has circular references: $sv\n" if has_circular_ref($sv); } @$all=(); } END { find_all_cycles() }

Hope this helps,
-- Hauke D

Replies are listed 'Best First'.
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 15:03 UTC
    Interesting stuff you bring up.

    But just in order not to be misunderstood:

    I provided the example just to illustrate how destructors can run after END-blocks.

    In this particular case the problem is of couse the circular reference (that I put there simply to force this behaviour).

    My question is not how to fix this particular case but if there was a way to ensure something is run at the very end, in particular when using some unknown code that you do not want to fiddle with.

      Hi morgon,

      My question is not how to fix this particular case but if there was a way to ensure something is run at the very end, in particular when using some unknown code that you do not want to fiddle with.

      Ah, I see - in that case it was still an interesting exercise. If you don't have circular references, then I believe END blocks are the answer, whereas if you do have circular references, that's what I might attack first. (Update: See Re^6: is there a way to ensure some code is the last thing that is run?)

      So, do I understand correctly you don't have a specific case where you're having problems, and this was a theoretical exercise?

      Regards,
      -- Hauke D

      Morgon, interesting discussion you started.

      I think the best you can do is to focus on "seen first, run last" and "end of scope".

      Using those 2 points, I think the following is the closest to what you are asking for.

      #!perl # Make this END block the first thing compiled to insure it # will be the last END block to run. END { # any special clean up goes here warn 'END - Bypassing final global Perl clean up'; # $? is the value passed to exit() unless modified # in another END block. POSIX::_exit($?); # WARNING - Any DESTRUCTions that could not be # completed before _exit() is called will NOT # be completed } # Create an extra layer of scope { use strict; use warnings; use Some::Module; my $s = Some::Module->new(); } # Close extra scope to trigger most of the DESTRUCTion exit(0); # Here, the various END blocks will run in reverse order # of being compiled, concluding with the END block at the # top of this file.

      Disclaimer: Not tested. YMMV.