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

Fellow monks,

is there a way to manipulate the order in which perl executes several END blocks? Let's assume that I'm using a module SomeModule that defines an END block and I'm also defining an END block in the main program:

package SomeModule; END { print "End of SomeModule\n"; } package main; END { print "End of main\n"; }
This will execute both end blocks in this order:
End of main End of SomeModule
Now, what if SomeModule's END block does something undesirable, what's the best way for me to jump in after that?

Changing SomeModule's code isn't an option, it needs to happen in main.

Replies are listed 'Best First'.
Re: Order of END blocks
by meetraz (Hermit) on May 28, 2004 at 18:32 UTC
    According to perldoc perlmod:

    You may have multiple "END" blocks within a file--they will execute in reverse order of definition; that is: last in, first out (L +IFO).

    I'm pretty sure what "package" the end blocks are defined in does not matter. They are executed purely in the reverse order of definition, as the documentation says.

    Does this answer your question?

      Let's split it up into two files, then, here's SomeModule.pm:
      # SomeModule.pm package SomeModule; END { print "End of SomeModule\n"; } 1;
      And here's a test script test.pl:
      # test.pl use SomeModule; END { print "End of main\n"; }
      Any way to jump in after SomeModule's END block?
        Yes, and Zaxo has it right. If you want one end block to execute after another, it must be defined before the other. The packages or file separation are insignificant.
Re: Order of END blocks
by Zaxo (Archbishop) on May 28, 2004 at 18:48 UTC

    Define main's END block before using the module to reverse their order,

    END { print 'End of main.', $/; } use SomeModule; # ... __END__ End of SomeModule End of main

    After Compline,
    Zaxo

Re: Order of END blocks
by gmpassos (Priest) on May 28, 2004 at 20:27 UTC
    A END blocks after being defined can't be changed! As mentioned in perlmod, they will be called on global destruction in reverse order (Last to In First to Out - LIFO).

    The only way to change this END block is to do some perlhack, building a XS module. I have worked on that to can call END block at runtime, and to undef the END blocks of a package that I already have called:

    XS

    void get_end_subs( pkg ) char * pkg PREINIT: CV *cv_end ; PPCODE: if ( PL_endav ) { long i ; SV **svp = av_fetch(PL_endav , 0 , FALSE) ; long lng = av_len( PL_endav ) ; EXTEND(SP, lng + 1 ) ; for( i = 0 ; i <= lng ; ++i ) { cv_end = (CV *) svp[i] ; if ( !strcmp( HvNAME(CvSTASH(cv_end)) , pkg) ) { PUSHs( newRV_inc( cv_end ) ); } } } void undef_end_subs( pkg ) char * pkg PREINIT: CV *cv_end ; CV *cv_null ; PPCODE: if ( PL_endav ) { long i ; SV **svp = av_fetch(PL_endav , 0 , FALSE) ; long lng = av_len( PL_endav ) ; cv_null = get_cv("Safe::World::NULL_END_SUB" , TRUE) ; for( i = 0 ; i <= lng ; ++i ) { cv_end = (CV *) svp[i] ; if ( !strcmp( HvNAME(CvSTASH(cv_end)) , pkg) ) { cv_undef( cv_end ) ; svp[i] = cv_null ; SvREFCNT_inc( cv_null ) ; } } }
    The undef_end_subs() function will set the END blocks that I don't want to have them executed to a null SUB defined at &Safe::World::NULL_END_SUB.

    And here's a example of use:

    my $packname = 'foo::bar' ; foreach my $end_i ( get_end_subs($packname) ) { eval{ &$end_i() if $end_i ; } ; } undef_end_subs($packname) ;
    I think that you can get this sources and change to do what you want, let's say, change the order of the END blocks at PL_endav, etc...

    Note that PL_endav is a AV, so, you can build a XS function that return it as a normal Perl ARRAY, and do all your changes with Perl code, but you also will need a function that get informations about the values in this array to know if it's the right END block (CODE).

    Also you can use the B module to do that. If you've got a recent enough version of perl (5.7+ IIRC) you already have the module. Example of use:

    use B; sub B::exec_ENDs { my $pkg = caller; $_->object_2svref->() for grep { $_->GV->STASH->NAME eq $pkg } B::end_av->ARRAY; } { package foo; sub pretend_to_leave { B->exec_ENDs(); print "I'll go get my coat ...\n"; } END { print "... another END block\n" } END { print "foo: this will be followed by " } } foo->pretend_to_leave(); END { print "main: Right, I'm outta here\n" } __output__ foo: this will be followed by ... another END block I'll go get my coat ... main: Right, I'm outta here foo: this will be followed by ... another END block
    Enjoy! ;-P

    Graciliano M. P.
    "Creativity is the expression of the liberty".