Wiley.Symbian.OS.C.plus.plus.for.Mobile.Phones.Aug.2007 (779890), страница 38
Текст из файла (страница 38)
Once a request is completed and the thread’s requestsemaphore is signaled, the thread awakes. The usual cause of a straysignal in this kind of scenario is that only one request completed andtherefore one was still pending.Let’s assume that the thread request was completed, but the timerrequest is still pending. This means that at some time in the future, thetimer’s request will expire. If control returns to the active scheduler, thethread’s request semaphore is signaled when the timer request expires.However, there is no active object upon which to call RunL() becausewe didn’t use active objects to make the timer request.
This means thatthe active scheduler cannot locate any suitable active object to dispatchand panics with a stray signal.In this particular case, the problem can be solved by checking whichrequest is pending and then issuing a cancel request to the appropriateservice provider. When canceling the five-second timer, for example, youmust also call User::WaitForRequest(timerStatus) to ensurethat you ‘eat’ the associated timer completion event, otherwise theOTHER COMMON ACTIVE OBJECT ERRORS187scheduler will be confused and think the event is associated with anactive object. Mixing active objects with calls to the User::WaitForRequest() functions is not recommended.Removing an Uncompleted Active Object from the QueueWhen the request semaphore for your thread is signaled, the activescheduler assumes that the event signal relates to an active object.
If youcall CActive::Deque() to remove your active object from the activescheduler before its request completes, by calling CActive::Deque(),then the active scheduler cannot find your object.Calling a Leaving Function after an Asynchronous RequestThis is really a variation on forgetting to set an object as active.In the following code, the implementation of the service providercalls a leaving function after an asynchronous request has been initiated.With an aDelay argument of zero, the asynchronous request is initiatedand the asynchronous service provider leaves with KErrArgument.This means that the call to SetActive() is skipped and when theasynchronous request eventually completes, the active scheduler raises astray-signal panic since it cannot identify that the object actually madea request.void RAsyncProvider::NotifyAfterDelayL(TRequestStatus& aStatus,TTimeIntervalSeconds aDelay){aStatus = KRequestPending;SendReceive( ENotifyOnDelay, aStatus);if (aDelay == 0){// Using a delay of zero is invalidUser::Leave(KErrArgument);}}void CMyDelayActiveObject::RequestDelayL(){iDelayProvider.NotifyAfterDelayL(iStatus, TTimeIntervalSeconds(0));SetActive();}6.9 Other Common Active Object ErrorsThere are some other frequent mistakes made when working with activeobjects.
This section considers some of the more common problems thathave been observed.188ACTIVE OBJECTSCalling DoCancel() DirectlyThe DoCancel() function is meant to be called indirectly via the publicnon-virtual CActive::Cancel() function.If you call DoCancel() directly, then you are not allowing the activeobject framework a chance to ‘eat’ the request semaphore signal thatoccurs when the asynchronous request is cancelled.
This confuses thescheduler into believing that your active object is now ready to run,thereby causing the RunL() method to be called even though youintended to cancel it. In this situation, when RunL() is called, iStatusis likely to be set to an error value (typically KErrCancel).Failing to Call Cancel() from Within a DestructorActive objects should almost always call Cancel() in their destructor.If your object happens to be active at the time of destruction, failing tocall Cancel() causes the CActive base class destructor to panic yourthread with E32USER-CBase 40.In addition, Cancel() should usually be called prior to executingany other code within your destructor, such as deleting class members.This ensures that all resources required to fully cancel any outstandingasynchronous request are still available when the virtual DoCancel()method is invoked.Checking IsActive() before Calling Cancel()if (IsActive()){Cancel();}The framework CActive::Cancel() function already does thisinternally, so there is no added value from doing it yourself.Failing to Implement DoCancel() CorrectlyIf your active object makes an asynchronous request, then your DoCancel() method should typically cancel that request by calling your serviceprovider’s synchronous cancel function.If you provide an empty implementation of DoCancel() then shouldthe active object’s Cancel() method be called when the object is active,your thread will be blocked until the asynchronous service providercompletes the request status.
As we saw, CActive::Cancel() waitsfor your object’s iStatus to be completed, hence the thread is blockeduntil this occurs.OTHER COMMON ACTIVE OBJECT ERRORS189Omitting Error HandlingA common problem, most often seen when using asynchronous notification APIs, is that the RunL() method of the active object does notcheck the iStatus completion code for an error. Instead, the error isignored and the active object just re-issues the same request to the sameasynchronous service provider.
In such a case, each subsequent requestis likely to complete with an error and the resulting ‘flat-spin’ cycle cancontinue indefinitely.If the priority of the active object is high, then it may well cause otherobjects to be starved of active scheduler time. Even if the active objecthas a suitably low priority, the process can quickly result in a drainedbattery, as the CPU will have little time to sleep.Using a TRAP Macro Inside RunL()The active scheduler places a TRAP harness around all calls to an activeobject’s RunL() method. If the RunL() method should leave, then, aswe have discussed, RunError() is invoked to handle the error.Whilst not technically a mistake, adding additional TRAP statementsinside RunL() may be an unnecessary overhead. A more refined solutionmay be to remove the TRAP and handle error situations via RunError().Calling Leaving Code in a Self-destructing RunL() MethodIf your RunL() implementation deletes its own active object instance, forexample, by calling delete this, then logically this should typicallybe the last line within the RunL() method.Were the RunL() method to delete the object and somehow leave,then the active scheduler TRAPs the leave and attempts to call RunError() on the object in question.
Since the object has deleted itself,this results in an exception.Using operator=() to Assign TRequestStatusThe CActive::iStatus member, an instance of the TRequestStatus class, is rarely meant to be assigned a value directly. Thereis one exception: the asynchronous service provider typically sets aTRequestStatus argument to KRequestPending during the initiation of an asynchronous request.However, it is rarely appropriate to assign a TRequestStatus objecta value directly. Instead, the request status should be assigned a valueas part of the request completion phase. For components that implementtheir own thread-local active objects, a request status should be completedvia User::RequestComplete().190ACTIVE OBJECTSLeaving Asynchronous MethodsIt is generally better to make asynchronous methods non-leaving. Thisavoids overly complex client code whereby clients need to TRAP calls tothe function and handle scenarios in the case of a leave.
Furthermore, forclients of such methods, there is always some uncertainty as to whetherthe function left before or after the asynchronous request was issued.Returning Error Values from an Asynchronous MethodShould an error situation exist, it is preferable to report the error viathe TRequestStatus argument rather than explicitly returning an errorcode.void RAsyncProvider::NotifyAfterDelay(TRequestStatus& aStatus,TTimeIntervalSeconds aDelay){if (aDelay == 0){// Using a delay of zero is invalidTRequestStatus* status = &aStatus;User::RequestComplete(status, KErrArgument);}else{// continue with normal asynchronous processing}// ...}If we assume that the client of this API is using an active object, thenthe above implementation means that clients of this API need to handleerror situations only via their RunL() function.An alternative, less robust, implementation of the request functionmight return an error value under certain circumstances (such as parameter validation) and additionally handle other potential errors during therequest processing (for example, due to lack of memory).
In such a scenario, the client must handle errors in two places: the direct return valuefrom the asynchronous request function and also from within the RunL()implementation.6.10Implementing State MachinesActive objects can be used to implement state machines. As an example,say we wish to amalgamate the CDelayedHello and CFlashingHellofunctionality of Section 6.3 and so produce the effect of the message flashing on and off once followed by displaying the message. This cycle repeatsIMPLEMENTING STATE MACHINESFigure 6.11191State machineuntil cancelled. This behavior can be represented by the state diagram inFigure 6.11.The state machine is initially in the Idle state and starting the machinemoves it into the Show Hello state. The greeting is shown and a timeris started. When the timer completes, it notifies the state machine andit moves to the Hide Hello state – the greeting is removed.