[Date Prev][Date Next][Thread Prev][Thread Next][Author Index][Date Index][Thread Index]

Constructor bombs for copy objects.



Date: Tue, 26 Jun 90 20:23:23 PDT
   From: xanadu!michael (Michael McClary)

   ...

   A constructor bomb is needed IFF a BLAST that occurs in the constructor
   >or anything it calls< isn't caught until it reaches a SHIELD outside the
   constructor.

   ... Constructor bombs:
    - Take over the normal call to the memory allocator,
    - Arm themselves when they receive the memory from (operator) new, and:
      - disarm themselves quietly if the constructor returns normally, or
      - free the storage as the exception passes up out of the constructor.

Hmmm...  Had a disturbing thought.  Lets say we have:


class Base {
    ...
    Base ();
    ~Base ();
};

class Derived {
    ...
    Derived ();
    ~Derived ();
};

Derived::Derived ()
      : Base () // unnecessary, but here for explicitness
{
    PLANT_CONSTRUCTOR_BOMB();
    x = zip (); /* something which would need to get cleaned up */
    foo (); /* a call which BLASTs in our example */
    y = zap (); /* something which would need to get cleaned up */
    bar (); /* may BLAST, but doesn't in our example */
}

In the Bomb package, when foo BLASTs, the sequence of actions is:
	Derived's destructor gets called
		It presumably cleans up after zip but not zap
	Base's destructor gets called
	The appropriate "operator delete ()" gets called.

Coding the constructor & destructor so the destructor knows how much
to clean up is quite possible (Michael has a recommended technique),
but is quite error prone and has some overhead.

In Bjarne's original exception handling proposal (the circulated
paper), the sequence we worried he was specifying was:
	Base's destructor gets called

The big problem is that it doesn't deallocate the memory.  What about
the fact that Derived's destructor is getting skipped?  Originally
Michael convinced me that the Bomb package was doing the correct thing
and Bjarne was making a mistake.  I now feel that the correct
sequence, and the one we should recommend to the standardization
effort, is:
	Base's destructor gets called
	The appropriate "operator delete ()" gets called.

When foo() BLASTs, we have a partially constructed Derived, & a fully
constructed Base.  Any cleaning up of the partial construction should
be done by planting, arming, and disarming bombs in Derived's
constructor.  Then destructors can know they only need to deal with
fully constructed objects.

Derived::Derived ()
      : Base () // unnecessary, but here for explicitness
{
    PLANT_CONSTRUCTOR_BOMB();
    x = zip (); 
    PLANT_BOMB(zipCleaner,zip); /* to clean up after zip */
    ARM_BOMB(zip,x);
    foo (); 
    y = zap ();
    PLANT_BOMB(zapCleaner,zap); /* to clean up after zap */
    ARM_BOMB(zap,x);
    bar ();
    DISARM_BOMB(zip);
    DISARM_BOMB(zap);
}

Unfortunately, I see no way for the bomb package to cause Base's
destructor to be invoked & to deallocate the object, but not to invoke
Derived's destructor.  As far as I can tell, this can only be
implemented by the language.  This means that there is nothing we can
do to make the Bomb package compatable with what we should recommend.
I also do not know whether or not there are any implementation
problems with implementing this proposal efficiently.

Interestingly, in the new A.R.M. book, this issue is left much more
ambiguous than in Bjarne's original paper.  I suspect this is a result
of the letter Michael & I sent him.  How ironic if Bjarne was more
right in the first place.

P.S.  What Bjarne proposes doing for member initializers which BLAST
has always seemed exactly right, is unfortunately impossible to
implement other than by the language, and therefore is an impossible
to fix problem with the current Bomb package.  This is non-fatal, as
until this is fixed we must simply avoid member initializers which may
BLAST.  (At the moment we cannot even generate members initializers
from the Smalltalk translator, so this isn't a big worry.)