Exception Handling and Cleanup Mechanism in Symbian
Introduction
Symbian handles Exceptions very differently compare to standard C++'s try and catch mechanism. Symbian has provided some cleanup mechanism. Symbian has TRAP/TRAPD along with CleanupStack and some conventions like Leaving methods which we should know for understanding Cleanup mechanism in Symbian. Tills Symbian 9.1, Symbian C++ was not supporting try and catch mechanism of standard C++. But, from Symbian 9.1 onwards, it has support for try and catch as well. But Symbian recommends the use of its own cleanup mechanism over try and catch.
Leaving mehods and TRAP
If a function name ends with L, then that means that, this function may leave (can throw an exception).
If a function name ends with LC, then that means that, this function may leave and it will also push one item on cleanup stack.
If a function name ends with LD, then that means that, this function may leave and it destroys (deletes/releases) the item which is been pushed on to stack.
Never use a LC function in TRAP/TRAPD macro and the reason is, in TRAP macro, it marks the cleanup stack, calls the function passed and it expects all cleanup items to be popped from cleanup stack which are pushed onto cleanup stack in this TRAP's scope. Function ending with LC will push one item on to stack which should be popped by the caller of this function. But when we call LC function inside TRAP/TRAPD, then there will be one cleanup item still remaining in cleanupstack which we can not destroy. So it will PANIC with E32User Panic 71.
CleanupStack and TRAP/TRAPD
Each executing unit (or thread) should have its own CleanupStack and a toplevel TRAP/TRAPD for exception handling and cleanup, if it is calling atleast one Leaving methods or allocating some resources. To give more in-depth on these requirements, assume the code below:
{
//Do something here.
}
void DoExampleL()
{
CConsoleBase* console;
// Make the console and push it on the cleanup stack.
console = Console::NewL(_L("Console"), TSize( KConsFullScreen, KConsFullScreen));
CleanupStack::PushL(console);
DoTheThingsForMeL(console);
CleanupStack::PopAndDestroy(console);
}
TInt E32Main()
{
__UHEAP_MARK;
//Create a cleanup stack
CTrapCleanup* cleanup = CTrapCleanup::New();
//Call some Leaving methods inside TRAP
TRAPD(error, DoExampleL());
__ASSERT_ALWAYS(!error, User::Panic(KAPConsoleTest, error));
//Destroy cleanup stack
delete cleanup;
__UHEAP_MARKEND;
return 0;
}
If there is no TRAPD in main while calling DoExampleL() then the application will PANIC with EClnPushAtLevelZero as "any operation on CleanupStack expects that there is at least one TRAP marker". If not, then CCleanup::PushL() will Panic with EClnPushAtLevelZero telling its in the level ZERO (There is no TRAP at all).
If you see the definition of TRAP/TRAPD macro, you can see that, before executing the function specified, it will mark the CleanupStack, and executes the function and after that, it checks whether any more items are left in the CleanupStack that are pushed on with in the scope of this TRAP/TRAPS. If so, it will PANIC with E32user-CBase71. Similarly when we try to Pop or PopAndDestroy too many items from CleanupStack for the current nested level, then it will PANIC with E32user-CBase63 Similarly, if you don't create CleanupStack (if you comment out CTrapCleanup* cleanup = CTrapCLeanup::New()) then again, CleanupStack::PushL() will PANIC with EClnNoTrapHandlerInstalled.
But, TRAP/TRAPD and creation of cleanup stack: all these stuffs are not required, if you are not going to use any resources which may Leave or generate exceptions. Below code executes without any problem even though we have not created any CleanupStack or not used TRAP/TRAPD:
{
TInt array[10];
for(TInt i=0; i<10; i++)
{
array[i] = i+1;
}
return 0;
}
Bad Coding Practice
Don't use these (below) kinds of codes in your application:
if (err==KErrNone)
{
...
TRAPD(err, SomeLeavingFunL)
...
}
...
User::LeaveIfError(err);
In this, we will loose the value of err which we have used inside inner TRAPD. We will loose the scope of that once after that block. So either use different variable names in each TRAPD or use TRAP with same variable.
Again, it is not recommended to use multiple TRAP/TRAPD macros inside a function as TRAP and TRAPD's are heavy operations. Minimize the use to TRAPs as much less as possible. For example we can always re-write the code below in better way:
TRAPD(err1, CallLeavingMethodL());
TRAPD(err2, CallLeavingMethodL());
TRAPD(err3, CallLeavingMethodL());
TRAPD(err4, CallLeavingMethodL());
return err1 || err2 || err3 || err4;
}
Above code can be re-written as:
CallLeavingMethodL();
CallLeavingMethodL();
CallLeavingMethodL();
CallLeavingMethodL();
}
TInt SomeFunction() {
TRAPD(err, SomeFunctionL());
return err;
}
Threads and Exception Handling
We can create threads using RThread::Create API. There are two variations of this API as below:
TInt Create(const TDesC& aName, TThreadFunction aFunction, TInt aStackSize, RAllocator* aHeap, TAny* aPtr, TOwnerType aType = EOwnerProcess);
By default each newly created thread will have its own Heap. It wont share the Heap of the parent thread (the one which created the new thread) unless and until we explicitly do so.
Using first variation of Create API, we can not create a Thread which will share parent thread's heap. For achieving this requirement, we should use 2nd variation of create. We should pass aHeap as NULL, so that it will use Parent thread's heap for memory allocation. This API also gives us a option of creating a heap and sending it to the newly creating thread.
When you create a thread which shares parent threads heap, make sure that you have released all memory those are allocated by this thread. Else, when this thread terminates, there will be some memory which was allocated by the thread which is not yet released. If the main thread runs forever, this memory which is not released can't be used by the main thread, leading to memory leak.
When you create a thread with its own heap, and in case you have forgot to release all memory allocated, then while the thread terminates, it will release all allocated releases, including heap memory. (But, even in this case, its not recommended to terminate a thread without releasing the allocated resources)
When you create a Thread, make sure that:
You have a TRAP/TRAPD in your thread which is operates on top level Leaving function, if in case you have any
You have created a Cleanup Stack, if in case you are using TRAP/TRAPD or any Leaving method in your thread
You have installed a Scheduler if in case you are going to use Asynchronous Request ( To be very precious, Active objects)
Lets say we have created a thread which uses cleanup stack and has to handle many asynchronous request, in that case, the ThreadEntryFunction should do something like this before doing any other operations:
{
TInt retVal = KErrNoMemory;
// Create a Cleanup Stack for this Thread
CTrapCleanup* cleanupStack = CTrapCleanup::New();
if( cleanupStack )
{
// Create a Scheduler for this Thread and Install it
CActiveScheduler* scheduler = new CActiveScheduler;
if( scheduler )
{
CActiveScheduler::Install( scheduler );
TRAP( retVal, retVal = DoSomeThingForMeL( aParam ) );
delete scheduler;
}
delete cleanupStack;
}
return retVal;
}
Above code is an entry point to newly created thread. So, here we should take care of doing all necessary operations for the thread execution. So, I have created a CleanupStack using CTrapCleanup::New() method, then created and installed an ActiveScheduler and then I am calling a leaving function with in TRAP macro so that I can catch any exception that may happen inside DoSomeThingForMeL() function. As, I have a CleanupStack for the new thread, it can use CleanupStack for pushing and popping cleanup items using static APIs provided by CleanupStack class.
CleanupStack static Push/Pop APIs
CleanupStack provides collection of static functions that are used to add resources to and remove resources from the cleanup stack. There are 3 statis APIs for pushing cleanup items on to cleanup stack:
void PushL(CBase* aPtr);
void PushL(TCleanupItem anItem);
Some of the static Pop APIs are:
void Pop(TInt aCount);
void PopAndDestroy();
void PopAndDestroy(TInt aCount);
Pop primitives pops an object or a specified number of objects previously pushed onto the cleanup stack by CleanupStack::PushL(). PopAndDestroy variants pops and cleans up an item/a specified number of objects pushed onto the stack.
Apart from APIs provided by CleanupStack class, there are several inline functions defined in e32base.h for pushing cleanup items on to stack. Below inline functions constructs and pushes a TCleanupItem object onto the cleanup stack:
CleanupClosePushL(T& aRef)
The cleanup operation is the private static function Close() of the templated class CleanupClose and is invoked as a result of a subsequent call to CleanupStack::PopAndDestroy(). Example:
CleanupClosePushL( semaphore );
CleanupStack::PopAndDestroy( &semaphore );
CleanupDeletePushL(T* aPtr)
The cleanup operation is the private static function Delete() of the templated class CleanupDelete, and is called as a result of a subsequent call to CleanupStack::PopAndDestroy(). Example:
CleanupDeletePushL( pHeap );
CleanupStack::PopAndDestroy( pHeap );
CleanupReleasePushL(T& aRef)
The cleanup operation is the private static function Release() of the templated class CleanupRelease and is invoked as a result of a subsequent call to CleanupStack::PopAndDestroy(). Example:
CleanupReleasePushL( fileBuf );
CleanupStack::PopAndDestroy( &fileBuf );
CleanupArrayDeletePushL(T* aPtr)
The cleanup operation is the private static function ArrayDelete() of the templated class CleanupArrayDelete, and is called as a result of a subsequent call to CleanupStack::PopAndDestroy(). Example:
CleanupArrayDeletePushL( pIntArray );
CleanupStack::PopAndDestroy( pIntArray );
CleanupStack::PushL() may leave. PushL will make sure that the object is pushed onto the cleanup stack properly. This operation will make sure that there will be a free slot in the cleanupstack for the next Push operation. If there won't be any memory for next push (if memory cannot be allocated) then PushL will cleans the stack as normal and leaves. So, programmer doesn't have to do anything additional if in case PushL leaves.






Exception Handling and Cleanup Mechanism in Symbian
Exception Handling and Cleanup Mechanism in Symbian