Symbian OS Explained - Effective C++ Programming For Smartphones (2005) (779885), страница 29
Текст из файла (страница 29)
An object implementing such methods is usuallyknown as an asynchronous service provider.A typical active object class provides public ”request issuer” methodsfor its clients to submit asynchronous requests. These pass on the requeststo the encapsulated asynchronous service provider, passing by referencethe iStatus member variable of the CActive class as the TRequestStatus parameter. Having issued the request, the issuer method mustcall CActive::SetActive() to set the iActive member to indicatethat there is an outstanding request.The service provider must set the value of the incoming TRequestStatus to KRequestPending (=0x80000001) before acting on therequest.
Upon completion, if the service provider is in the same threadas the requester, it calls User::RequestComplete(), passing theTRequestStatus and a completion result, typically one of the standarderrors such as KErrNone or KErrNotFound, to indicate the successor otherwise of the request. User::RequestComplete() sets thevalue of TRequestStatus and generates a completion event in therequesting thread by signaling the thread’s request semaphore. If theasynchronous service provider and the requester are in separate threads,the service provider must use an RThread object, representing a handle to the requesting thread, to complete the request.
It should callRThread::RequestComplete() to post the completion code andnotify the request semaphore.While the request is outstanding, the requesting thread runs in theactive scheduler’s event processing loop. When it is not handlingcompletion events, the active scheduler suspends the thread by calling User::WaitForAnyRequest(), which waits on a signal to thethread’s request semaphore. When the asynchronous service providercompletes a request, it signals the semaphore of the requesting threadas described above, and the active scheduler determines which activeACTIVE OBJECT BASICS129object should handle the completed request. It uses its priority-orderedlist of active objects, inspecting each one in turn to determine whetherit has a request outstanding.
It does so by checking the iActive flag;if the object does indeed have an outstanding request, it then inspectsits TRequestStatus member variable to see if it is set to a value otherthan KRequestPending. If so, this indicates that the active object isassociated with a request that has completed and that its event handlercode should be called.Having found a suitable active object, the active scheduler clears theactive object’s iActive flag and calls its RunL() event handler. Thismethod handles the event and may, for example, resubmit a request orgenerate an event on another object in the system.
While this method isrunning, other events may be generated but RunL() is not pre-empted – itruns to completion before the active scheduler resumes control anddetermines whether any other requests have completed.Once the RunL() call has finished, the active scheduler re-enters theevent processing wait loop by issuing another User::WaitForAnyRequest() call. This checks the request semaphore and either suspendsthe thread (if no other requests have completed in the meantime) orreturns immediately (if the semaphore indicates that other events weregenerated while the previous event handler was running) so the schedulercan repeat active object lookup and event handling.Here’s some pseudo-code which represents the basic actions of theactive scheduler’s event processing loop.EventProcessingLoop(){// Suspend the thread until an event occursUser::WaitForAnyRequest();// Thread wakes when the request semaphore is signaled// Inspect each active object added to the scheduler,// in order of decreasing priority// Call the event handler of the first which is active & completedFOREVER{// Get the next active object in the priority queueif (activeObject->IsActive())&& (activeObject->iStatus!=KRequestPending){// Found an active object ready to handle an event// Reset the iActive status to indicate it is not activeactiveObject->iActive = EFalse;// Call the active object’s event handler in a TRAPTRAPD(r, activeObject->RunL());if (KErrNone!=r){// event handler left, call RunError() on active objectr = activeObject->RunError();if (KErrNone!=r) // RunError() didn’t handle the error,Error(r);// call CActiveScheduler::Error()}break; // Event handled.
Break out of lookup loop & resume130ACTIVE OBJECTS UNDER THE HOOD}} //End of FOREVER loop}If a single request has completed on the thread in the interim, theactive scheduler performs lookup and calls the appropriate event handleron that active object. If more than one request has completed in that time,the active scheduler calls the event handler for the highest priority activeobject. It follows that, if multiple events are generated in close successionwhile another event is being handled, those events may not be handledin the sequence in which they occurred because the active object searchlist is ordered by priority to support responsive event-handling.Normally, active object code should be designed so the priority doesnot matter, otherwise the system can become rather delicate and bethrown off balance by minor changes or additional active objects on thethread. However, to be responsive, say for user input, it is sometimesnecessary to use a higher priority value.
Long-running, incremental tasks,on the other hand, should have a lower priority than standard sincethey are designed to use idle processor time (as I’ll describe later inthis chapter).It’s important to understand that the priority value is only an indicationof the order in which the active scheduler performs lookup and eventhandling when multiple events have completed. In contrast to the priorityvalues of threads used by the kernel scheduler, it does not represent anability to pre-empt other active objects. Thus, if you assign a particularactive object a very high priority, and it completes while a lower-priorityactive object is handling an event, no pre-emption occurs. The RunL()of the lower-priority object runs to completion, regardless of the fact thatit is ”holding up” the handler for the higher-priority object.On Symbian OS, you cannot use active object priorities to achievea guaranteed response time; for this you must use the pre-emptivescheduling associated with threads1 , which is described in Chapter 10.If you have a large number of active objects in a single thread whichcomplete often, they ”compete” for their event handler to be run bythe active scheduler.
If some of the active objects have high prioritiesand receive frequent completion events, those with lower priorities waitindefinitely until the active scheduler can call their RunL() methods.In effect, it’s possible to ”hang” lower-priority active objects by addingthem to an active scheduler upon which a number of high-priority activeobjects are completing.1The new hard real-time kernel in Symbian OS 8.0 can commit to a particular responsetime. On earlier versions of Symbian OS, the kernel has soft real-time capabilities andcannot make such guarantees.RESPONSIBILITIES OF AN ACTIVE OBJECT131An active object’s priority is only an indication of the order inwhich the active scheduler performs lookup and event-handling.
Itdoes not reflect an ability to pre-empt other active objects.9.2 Responsibilities of an Active ObjectFigure 9.1 illustrates the roles and actions of the active scheduler, activeobject and the asynchronous service provider. It extends Figure 8.1.ExecutableActive ObjectActive SchedulerAsynchronous ServiceProviderCreate and Install theActive SchedulerCreate Active Object(with appropriatepriority) and add it to theActive SchedulerIssue a request to theActive ObjectMake a request to the AsynchronousService Provider, passing in iStatusSets iStatus=KRequestPendingand starts the serviceCall SetActive()Start the ActiveScheduler if it is notalready startedWait LoopCActiveScheduler::Start()RunL() handles thecompleted eventand resubmitsanother request orstops the ActiveSchedulerCalls RunL() on the ActiveObject withiStatus!=KRequestPending and iActive=ETrueService completes and usesRequestComplete() tonotify the Active Scheduler(by signalling its threadsemaphore) and to post acompletion resultCActiveScheduler::Stop()PROCESS ORTHREADBOUNDARYFigure 9.1Roles and actions of the active scheduler, an active object and an asynchronous service provider132ACTIVE OBJECTS UNDER THE HOODIt looks complex, but I’ll explain how it all fits together throughout thischapter and you’ll probably want to refer back to it later.The following list summarizes the responsibilities of an activeobject:• As I described in Chapter 8, the priority of an active object must be seton construction.