Cleanup requirements

This document describes the requirements for cleanup after a function leaves.

When a function leaves, it transfers control directly to the statement following the TRAP (or TRAPD ) macro under which it was invoked. This is carried out by setting the stack pointer to the context of the original TRAP macro, and jumping to the desired program location. Therefore,

  • any objects created as automatic variables, passed by value as arguments, or created as member variables of other objects so created, will be orphaned : their destructor will not be called, and any resources they claim except for storage space on the stack, cannot be recovered.

This key aspect of Symbian platform exceptions has far-reaching implications:

  • There should be a clear distinction between objects which can be safely orphaned, and those which cannot.

    This is embodied in the naming convention for types. All types beginning with T can be safely orphaned, including, for instance, TInt , TPoint , TPtr and many others. Such objects can be freely allocated on the stack.

    The basic requirement for T objects is that all their data is contained internally. Pointers, handles and references to data owned by the T object are not allowed (although such references to data owned by other objects is allowed).

    C objects must never be orphaned: they should never be allocated on the stack.

    R objects may contain handles to external resources, but are generally designed so that the R object can be copied without copying its resources. Copied R objects may therefore be allocated on the stack: the stack-allocated copies may safely be orphaned, provided the resources are safely accessible by some other means.

  • Objects which cannot be safely orphaned must, if allocated inside the trap harness, be accessible somehow so they can be cleaned up.

The cleanup stack is the Symbian platform mechanism for handling this last problem.

Example

The problem for heap-allocated resources is shown below. If the call to DoSomethingL() leaves, the CExample object would be orphaned on the heap: the memory used for it could not have been recovered until the program terminates.

       
        
       
       void doExampleL()
 {
 // An T-type object: can be declared on the stack
 TBuf<10> buf;

 // A C-type object: must be allocated on the heap
 // Allocate and leave if can not
 CExample* myExample = new (ELeave) CExample;

 // do something that cannot leave: no protection needed
 myExample->iInt = 5; 

 // PROBLEM: do something that can leave
 myExample->DoSomethingL();
   
 // delete
 delete myExample;
 }