quick_recipes (779892), страница 7
Текст из файла (страница 7)
We’ll discuss how to work withSymbian OS threads later in this chapter.Symbian OS has a set of well-documented panic categories (forexample, KERN-EXEC, E32USER-CBASE, ALLOC, USER), the detailsof which can be found in the reference section of the Symbian DeveloperLibrary in the SDK.3.3.2TRAP and TRAPD MacrosThe TRAP and TRAPD macros can be used to ‘catch’ a leave and toextract the leave code integer.
If a leave occurs in a function called withina TRAP, control will return immediately to the TRAP harness macro.The TRAP macro requires that the integer variable has been definedbeforehand, while TRAPD defines the variable for you. The followingcode shows how to trap a leaving method:TInt leaveCode;TRAP(leaveCode, LeavingMethodL());if (KErrNone == leaveCode){// If no error, do something.}LEAVES AND EXCEPTION HANDLING35else{// If an error happened, handle it here.}Alternatively, using TRAPD, the code is expressed as follows:TRAPD(leaveCode, LeavingMethodL());if (KErrNone == leaveCode){// If no error, do something.}else{// If an error happened, handle it here.}You can find the list of Symbian OS system-wide error codes – which,apart from KErrNone (which is 0), are negative numbers – in \epoc32\include\e32err.h and in the Symbian Developer Library of eachSDK.TRAP macros can also be nested to catch and handle leaves at differentlevels of code, where they can best be dealt with.
However, each TRAPhas an impact on executable size and execution speed, and the numberof TRAPs should be minimized where possible.3.3.3 What Causes a Leave?If you are writing a function which must not leave, the TRAP macro mustbe used to prevent a potential leave from propagating. For example:TInt NonLeavingMethod(){TRAPD(err, LeavingMethodL());... // Handle the error}Since NonLeavingMethod() cannot leave, it must call LeavingMethodL() within a TRAP and handle the error internally, or return it.But what can cause a leave?A leave may occur in a function if it:• Explicitly calls one of the system functions in class User that cause aleave, such as User::Leave() or User::LeaveIfError().• Uses the Symbian OS-overloaded form of operator new, which takesELeave as a parameter.36SYMBIAN OS DEVELOPMENT BASICS• Calls code that may leave (for either of the reasons above) withoutusing a TRAP harness.Warning: User::Leave() doesn’t have a trailing ‘L’, yet it can leave.This is one of a few exceptions to the naming convention, and we willindicate others in this book as we come to them.3.3.4new(ELeave)Symbian OS overloads the global operator new to leave if there isinsufficient heap memory for successful allocation.
Use of this overloadallows the pointer returned from the allocation to be used without afurther test that the allocation was successful, because the allocationwould have left if it were not (in this respect, it is similar to the standardC++ new operator which throws an exception when allocation fails). Forexample:CBook* InitializeBookL(){CBook* book = new(ELeave) CBook();book->Initialize(); // No need to test book against NULLreturn (book);}3.4 The Cleanup StackIf memory is allocated on the heap and referenced only by a stack-basedlocal variable, what happens if a leave occurs? For example, consider thefollowing:void UnsafeFunctionL(){CAnotherClass* theObject = new (ELeave) CAnotherClass();theObject->InitializeL(); // Leave happens here.
. .// theObject will be orphaned on the heap!delete theObject ; // This will never be called}The memory allocated on the heap to store theObject will becomeinaccessible if the call to InitializeL() leaves. If that happens,theObject can never be deallocated (it is said to be ‘orphaned’),resulting in a memory leak. It is not ‘leave-safe’. One way to make thetheObject pointer leave-safe is to place a TRAP (or TRAPD) macroaround every potential leaving call that follows its creation.
However,THE CLEANUP STACK37the use of TRAPs should be limited for reasons of efficiency, and to avoidclumsy code which constantly traps leaves and checks leave codes.As a more sophisticated alternative, the Symbian OS cleanup stack canbe used to prevent this, by storing pointers to heap-based objects uponit before calling any method that may leave. The example above can berewritten using the cleanup stack as shown below:void SafeFunctionL(){CAnotherClass* theObject = new (ELeave) CAnotherClass();// Push to the cleanup stack before calling// any methods that may leave.CleanupStack::PushL(theObject);// If leave happens here, theObject will still be// deleted.theObject ->InitializeL();// Pop the object from the cleanup stack.CleanupStack::Pop(theObject);// And finally delete the object.delete theObject;// Alternatively, the last 2 lines of code can// be expressed in one line, as follows:// CleanupStack::PopAndDestroy(theObject);}In the event of a leave, the cleanup stack takes responsibility forreleasing the memory referenced by the pointers stored upon it.While heap variables referenced only by local variables are orphanedif a leave occurs, member variables do not suffer a similar fate (unless thedestructor neglects to delete them when it is called at some later point).The following code is safe:void CTestClass::SafeFunctionL(){// Heap allocation of a member variableiTheObject = new(ELeave) CAnotherClass();iTheObject ->InitializeL(); // Safe for iTheObject}The heap-based iTheObject member is stored safely, and deleted ata later stage with the rest of the CTestClass object, through the classdestructor.The cleanup stack can also be used to make instances of heapbased T classes leave-safe too.
You can also use it for R classes tomake them leave-safe, but R-class objects need to be pushed onto thecleanup stack differently, because their cleanup method (e.g., Close())38SYMBIAN OS DEVELOPMENT BASICSmust be called to release the resources they own. Instead of usingCleanupStack::PushL(), R-class objects are pushed using special global methods: CleanupClosePushL() or CleanupReleasePushL(), depending on how the object should be destroyed.
Forexample:RFs fileServer;User::LeaveIfError(fileServer.Connect());// Push file server handle to the cleanup stack.CleanupClosePushL(fileServer);// Call methods that may leave here.// ...// This will close the file server as well.CleanupStack::PopAndDestroy(fileServer);3.5 The Cleanup Stack FAQ: Advanced InformationThis section contains some advanced information that may answer someof the questions you have had while using the cleanup stack.
It’s a usefulreference, but if this is the first time that you’ve encountered the cleanupstack, you may prefer to skip ahead to the next section, and return herewhen you’ve had a chance to experiment with some code that uses it.3.5.1 Why Does PushL() Leave?In the examples above, you may have noticed that PushL() is a leavingfunction, which at first sight appears to be self-defeating.
If the cleanupstack is supposed to make pointers leave-safe, how can it cause a leavewhen you call it and yet still be helpful?The reason PushL() may leave is that it may need to allocate memoryto store the pointers passed to it (and in low-memory conditions this couldfail). However, the object passed to PushL() will not be orphaned ifit leaves. This is because, when the cleanup stack is created, it has atleast one spare slot.
When PushL() is called, the pointer is added toa vacant slot, and then, if there are no remaining slots available, thecleanup stack attempts to allocate some for future calls to PushL(). It isif that attempted allocation fails, that a leave occurs. The pointer passedin was stored safely before any leave could occur, so the object it refersto is safe, and is cleaned up as usual.3.5.2 Why Do I Get a Panic When I Try To Use the Cleanup Stack?The cleanup stack is only available to code executed inside a TRAPmacro. Don’t worry, most of the code you can modify is in that situation,THE CLEANUP STACK FAQ: ADVANCED INFORMATION39unless you are writing a console application or creating your own server.Trying to use CleanupStack::PushL() outside of any TRAP macrowill result in an E32User-CBase66 panic.3.5.3 Why Do I Get a Panic When My Code Leaves a TRAP?The cleanup stack must be emptied by the correct number of CleanupStack::Pop() calls before it is destroyed at the end of the TRAP macrothat created it or your application will panic.3.5.4 Why Do I Get a Panic When I Call CleanupStack::Pop()?There are several possible reasons for a panic when you call thePop() method.
For example, calling CleanupStack::Pop() whenthe cleanup stack is empty will result in yet another panic.Another reason could be that you are calling Pop() and passing inthe pointer you expect to be taken off the cleanup stack, but another isactually being popped instead. The cleanup stack is implemented as astack, so for any series of pushes, the corresponding pops must occur inreverse order.It is a good idea to name the pointer as it is popped, so debug buildscan check that it is the correct pointer and flag up any programmingerrors. For example:void TestUIQPhonesL(){// Each object is pushed onto the cleanup stack immediately// it is allocated, in case the next allocation leavesCUiq3Phone* phoneP1i = CUiqPhone::NewL(EP1i);CleanupStack::PushL(phoneP1i);CUiq3Phone* phoneM600i = CUiqPhone::NewL(EM600i);CleanupStack::PushL(phoneM600i);CUiq3Phone* phoneP990i = CUiqPhone::NewL(EP990i);CleanupStack::PushL(phoneP990i);... // Leaving functions called here// Various ways to remove the objects from the stack and delete them:// (1) All with one anonymous call – OK// CleanupStack::PopAndDestroy(3);// (2) Each object individually to verify the code logic// Note the reverse order of Pop() to PushL()// This is long-winded// CleanupStack::PopAndDestroy(phoneP990i);// CleanupStack::PopAndDestroy(phoneM600i);// CleanupStack::PopAndDestroy(phoneP1i);// (3) All at once, naming the last object - best solutionCleanupStack::PopAndDestroy(3, phoneP1i);}40SYMBIAN OS DEVELOPMENT BASICS3.5.5 How Do I Mix Leaves, Exceptions and the Cleanup Stack?Symbian OS TRAPs and leaves are implemented internally in terms ofC++ exceptions (a leave is a XLeaveException), and a TRAP willpanic if it catches any other kind of exception.