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

Lets say class C inherits from B and B inherits from A. But Z doesnt inherit from any of A,B,C. Now when I call exit(), let's say I have some cleanup code I want to run that depends on data in B and in Z. Thus, I need to know which of B or Z will be destroyed first.
  • Comment on order of destructor calls upon call of exit()

Replies are listed 'Best First'.
Re: order of destructor calls upon call of exit()
by merlyn (Sage) on Aug 15, 2000 at 17:53 UTC
    You can't know. Time to rethink your algorithm. The final DESTROYs happen in memory allocation order, nothing you can control.

    -- Randal L. Schwartz, Perl hacker

Re: order of destructor calls upon call of exit()
by tye (Sage) on Aug 15, 2000 at 18:11 UTC

    Have the Util object keep a reference to the FTPSession object (naturally, since it needs info from that object). Have the Util destructor write to STDOUT. Then you'll know that the FTPSession object still exists at that point.

    Note that this won't work if you store your objects in global variables nor if the script exits unexpectedly. You want the destructors to fire when "sub main" returns, right before "exit" is called. If the destructors fire during "global destruction", then the order of execution is out of your control.

    #!/usr/bin/perl -w use strict; use FTPSession; use Util; exit main(); [...] sub main { my $ftp= FTPSession->new(...); my $util= Util->new( $ftp ); [...] return 0; }
            - tye (but my friends call me "Tye")
Re: order of destructor calls upon call of exit()
by princepawn (Parson) on Aug 15, 2000 at 18:00 UTC
    Well, I dont know about this. I mean, the problem is this, we have a FTPSession class as base class for FTPSession::Win32 and FTPSession::Unix and of course the means of FTP'ing is done in Win32::Internet in the former and Net::FTP for the latter. We have another package, Util, in which we build up a string version of the log message we output to STDOUT. To save from the client having to tell us what the log msg says on STDOUT, we want to upload STDOUT to ourselves when the script is about to exit. Thus, we need FTP session information from the FTP module hierarchy and we need log information from the util package. If Perl had a rigorously defined protocol for the sequence in which objects were destroyed (I imagine Eiffel, Smalltalk and other such languages probably do...), then I could simply code my destructor with this knowledge.
Re: order of destructor calls upon call of exit()
by princepawn (Parson) on Aug 15, 2000 at 18:30 UTC
    This is an attempt to gain some summary facts about objects and destructors, some of which I dont believe was covered in the OOP Tour de Force "Object-Oriented Perl" by Damian Conway. So basically I have a set of things here which I am inducing must be true based on what tye and merlin have said, but am not sure and could not find the answer else where and some of which I just thought up myself:
    1. It may be wise to use signal handlers to deal with this situation? Or will all Perl data and functions be destroyed by the time the signal to end the Perl program is sent?
    2. If class A has a reference to class B, then even though Perl DESTROYs are done in mem alloc order (as stated by merlyn, we can gain some control over the order of destruction by maintaining a reference to class B. However, this reference must be lexically scoped? Why?!

      A Perl variable (such as a scalar, an array, but most-importantly an object) is destroyed when its reference count goes to zero. Reference counts usually go to zero when you leave the scope in which the object was declared (unless references to it still exist elsewhere) or right after the last object to have a reference to it is destroyed.

      This is very handy since if $a needs $b, then $a holds a reference to $b. This will mean that $a is destroyed before $b so that $a's DESTROY method can use $b rather safely. The "rather" is because there are some cases where this destruction order guarantee will not be honored.

      The biggest exception is global variables. The reference count on a global variable (A.K.A. a package variable or an "our" variable) never goes to zero. All globals are destroyed when the Perl interpretter is torn down in a phase called, dramatically enough, "global destruction". During this phase, all variables are destroyed without regard to reference counts and in an order that is probably best thought of as simply "undefined". The order is actually defined, but it has changed and probably will change again and isn't a particularly useful order from the script writer's perspective, so you'll have to go look it (well, them) up if you are really curious (that may be what merlyn was referring to when he talks about memory allocation order).

      I was worried that an unexpected call to die would also simply trigger global destruction, resulting in "random" destruction order again (that would be bad). However, testing shows that die does an orderly unwinding of the nested scopes, decrementing reference counts and destroying things in the desired order.

      Uncaught signals that kill your process will prevent the destruction of any and all Perl variables. The operating system will simply free the memory allocated to the process -- no Perl code will be given a chance to "clean up". So you'll want to catch likely signals and try as safely as possible to tell your program to shut itself down in an orderly manner. With proper use of destructors, a simple

      $SIG{HUP}= sub { die "Caught HUP\n" };
      is all it takes. It isn't completely safe but only because Perl signal handlers aren't completely safe no matter what they do.

      So I would recommend not doing destruction in signal handlers (die in the signal handler and let Perl do the destruction). I would also recommend not using END since such code usually needs to happen after you are sure you are done with some things but before you have destroyed other things. If you do clean up in destructors, then it usually just falls out that the clean up is done in the correct order and is done whether the program exiting normally or failed (if the operating system gives you that chance).

      Oh, and circular references will prevent members of the circle and things that they refer to from being destroyed until the global destruction phase. Note that even if Perl detected and destroyed circular references, the order of destruction would not be easy to predict since there is no "top" in a circular reference.

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