Wiley.Developing.Software.for.Symbian.OS.2nd.Edition.Dec.2007 (779887), страница 51
Текст из файла (страница 51)
Start() willreturn when an active object calls the active scheduler Stop()method. Make sure you have an event that will occur (i.e., at least oneactive object added to it, and with an event pending) or Start()will hang indefinitely. (See section 8.3.1.)• void Stop() causes the current active scheduler’s event loop toexit.•CActiveScheduler* Current() returns a pointer to the thread’scurrently installed active scheduler.8.3.4 Customizing the Active SchedulerCActiveScheduler is a concrete class and is normally created andused directly, without derivation. However, in some cases you may wantto derive your own active scheduler so that you can customize the eventloop or its start and stop functionality, and provide customized errorhandling for the scheduler.The following virtual methods are used when deriving your own activescheduler from CActiveScheduler:•virtual void OnStarting() is called from the Start() methodof the CActiveScheduler base class before the event loop is258ASYNCHRONOUS FUNCTIONS AND ACTIVE OBJECTSstarted.
The default implementation of OnStarting() does nothing,but your derived class can implement customized code that you wantto execute before starting the event loop.•virtual void OnStopping() is called from the Stop() methodof the CActiveScheduler base class. The default implementation ofOnStopping() does nothing, but your derived class can implementcustomized code you want executed upon stopping the active object’sevent loop.•virtual void Error(TInt aErr) is invoked by the active scheduler when a leave occurs within an active object’s RunL() function,and the active object itself did not handle the error in its ownRunError() method. The argument to Error() contains the leavecode.
Your derived scheduler object can override this function tohandle leaves that occur in a RunL() and are not handled by theactive object itself (see section 8.4). The default implementation ofError() is to raise an E32USER-CBase 47 panic.•virtual void WaitForAnyRequest() is called in the activescheduler’s event loop (i.e., it is initiated from the Start() method)and is used when waiting for an asynchronous function to complete. See the pseudo-code in section 8.3.2.
The default implementation of this function is to call User::WaitForRequest(),which blocks and waits at the current thread’s request semaphore.A derived CActiveScheduler can override this by implementing its own WaitForAnyRequest(). Normally this still involvescalling User::WaitForRequest(), but customized pre- or postprocessing of the event can be implemented here. Of course, youcould also handle events via a method other than the thread’s requestsemaphore (such as from a communication port, or as a network message). However, this would also require the attached active objects touse a set of customized asynchronous functions that were compatiblewith the customized event-handling method.8.4 Active Scheduler Error HandlingA leave can occur in your active object’s RunL() method (as indicatedby the L suffix) to indicate an error. By default, a leave in RunL()will generate a panic, but you can change this behavior for your activeobject by overriding CActive::RunError().
Additionally, you canoverride the default error handling of all active objects belonging tothe active scheduler by customizing the active scheduler Error()method – however, this is not often done.ACTIVE SCHEDULER ERROR HANDLING259CActive::RunError() is prototyped as:TInt RunError(TInt aErr)where aErr contains the leave code. The default base class implementation of RunError() returns the leave code passed to it.A customized RunError() should return KErrNone to indicate it hashandled the error. If RunError() returns a value other than KErrNone,the active scheduler assumes that the error has not been handled, andinvokes its own error-handling method, Error().Error() is a customizable method of CActiveScheduler, prototyped as:void Error(TInt aErr)where aErr is the value with which RunL() left or the value returnedfrom an active object’s RunError() method if it failed to handle a leavein RunL().The following pseudo-code illustrates how the active scheduler handlesRunL() leaves:// invoke RunL() for target active object and handle errorTRAPD(leaveCode,target_active_object->RunL());if (leaveCode != KErrNone){TInt rc = target_active_object-> RunError(leaveCode) ;if (rc !=KErrNone){Error(rc);// active object did not handle the// error, so call the active// scheduler’s own Error() function}}The default implementation of CActiveScheduler::Error() is togenerate an E32USER-CBase 47 panic.
Since the default implementationof CActive::RunError() is to return the RunL() leave code, thena E32USER-CBase 47 panic is what you will get on a leave if you donot override any of the error-handling functions. It is very undesirable foryour user to receive a panic like this, since it will close the application.You are certain not to pass Symbian Signed testing if it is observed as partof the testing process. So it makes sense to either ensure that an activeobject’s RunL() method cannot leave, or implement an error handlerin RunError() to ensure that the error is not propagated to the defaultactive scheduler.260ASYNCHRONOUS FUNCTIONS AND ACTIVE OBJECTS8.5 Active Object PrioritiesWhen an active object is constructed, a priority value is passed toCActive’s constructor.
The possible priority values (lowest to highest) are: EPriorityIdle, EPriorityLow, EPriorityStandard,EPriorityUserInput, EPriorityHigh. The priority value is normally specified in your derived active object’s constructor as follows:CDerivedActiveObject::CDerivedActiveObject(): CActive(CActive::EPriorityStandard){}If multiple active objects have outstanding asynchronous functions inprogress, and two or more of these functions complete at the same time,then the scheduler will see multiple active objects with iActive set toETrue and iStatus not equal to KRequestPending upon the nextsemaphore event.
In this case, the RunL() for the higher-priority activeobject is invoked. The scheduler then checks the request semaphore,sees the next event, and executes the RunL() of the next-highest priorityactive object that is still active, and so on.As an example, if three asynchronous functions produce events at thesame time – one high, one medium, and one low priority – then threetokens are added to the calling thread’s request semaphore. The schedulerprocesses these three events, one at a time, invoking first the high-priorityactive object’s RunL(), then the medium-priority one, and finally thelow-priority one. It does not really matter which semaphore signal (token)originally corresponded to which active object – they are all handled.8.6 Canceling Outstanding RequestsEach asynchronous function has a cancel function associated with it.This cancel function causes its corresponding asynchronous function toabort its operation and complete right away, with its TRequestStatusvariable set to KErrCancel.For example, the API class RTimer provides the asynchronous functionmethod RTimer::After(TRequestStatus& aStatus, TTimeIntervalMicroseconds32 aWait).
After the specified time has elapsed,this function will change aStatus to KErrNone and send the completion event (aStatus will be KRequestPending during the timeinterval, before the completion event). RTimer::Cancel() cancelsthe timer. Calling RTimer::Cancel() while the time interval is inprogress causes RTimer:: After() to complete right away, with itsTRequestStatus variable set to KErrCancel.As mentioned previously (see section 8.2.2), your derived active objectclass must override DoCancel() to cancel any pending asynchronousCANCELING OUTSTANDING REQUESTS261requests, and DoCancel() is never called directly, but is called byCActive’s Cancel() function. CActive::Cancel() checks if theactive object has an outstanding request, and if it does, it will invoke youroverridden DoCancel() method. After DoCancel() exits, Cancel()will call User::WaitForRequest() to consume the KErrCancelmessage.
Therefore, do not wait for the message in DoCancel() itself(or expect RunL() to be invoked in response to it).Cancel() should always be called in your active object’s destructor.If it is not, and you delete your object with an asynchronous request inprogress, you will get a stray signal panic.A common error for beginners, when canceling an asynchronousfunction, is to call User::WaitForRequest() from the DoCancel(), in order to consume the KErrCancel event directly. If youdo this, you will see Cancel() (and thus your destructor) hang, sinceCActive::Cancel() does its own User::WaitForRequest() toconsume the cancel event.Figure 8.2 shows how Cancel() and DoCancel() work.For example, if your active object uses the RTimer functions for itsasynchronous events, the DoCancel() could look as follows:void CDerivedActiveObject::DoCancel(){// Cancel the timeriTimer.Cancel();}Calling ThreadAsynchronousfunction inprogress...CActive::Cancel(){if (isActive()){DoCancel();User::WaitForRequest(iStatus);}}Figure 8.2CDerivedAO::DoCancel(){// Cancel Async Function}Functioncancels, sendscancel eventKErrCancelCancel() and DoCancel() Operation262ASYNCHRONOUS FUNCTIONS AND ACTIVE OBJECTS8.7 Removing an Active ObjectThe base class destructor removes the active object from the activescheduler’s list.