Wiley.Symbian.OS.C.plus.plus.for.Mobile.Phones.Aug.2007 (779890), страница 34
Текст из файла (страница 34)
ThisHOW IT WORKS167means that we must cancel the flashing text before calling CEikAppUI::Exit(). We destroy the active object from the destructor of itsowning class – this is the right place to destroy it, since the destructor iscalled in cleanup situations, while the command handler is not.The general rule is that you should always be careful about doinganything fancy from an active object’s DoCancel(). Nothing in aDoCancel() should leave or allocate resources and DoCancel()should complete very quickly.6.4 How It WorksIn this section, we look at the way an event-handling thread, with anactive scheduler and active objects, works. The asynchronous requestidiom and active object framework is built into Symbian OS at the verylowest levels, namely the user-side library, euser.dll, and the kernel.Every user-side thread can have a single installed active scheduler andany number of active objects registered with that active scheduler.The kernel’s representation of a user-side thread, called DThread,contains a pointer to the thread’s active scheduler.
Whilst the kernelmakes no use of this pointer itself, it is provided so that it is possible toaccess the active scheduler from a static user function. We have alreadyused this functionality in our earlier examples:CDelayedHello::CDelayedHello() : CActive(CActive::EPriorityStandard){CActiveScheduler::Add(this);}Calling the static CActiveScheduler::Add() function makes akernel-executive call to request the active-scheduler-object pointer forthe thread:EXPORT_C void CActiveScheduler::Add(CActive *aRequest){CActiveScheduler *pS=Exec::ActiveScheduler();__ASSERT_ALWAYS(pS!=NULL,Panic(EReqManagerDoesNotExist));__ASSERT_ALWAYS(aRequest,Panic(EReqNull));__ASSERT_ALWAYS(!aRequest->IsAdded(),Panic(EReqAlreadyAdded));pS->iActiveQ.Add(*aRequest);}This makes it convenient to access the active scheduler from anywherein your code without having to pass around a pointer to the objectexplicitly.
In fact, there are several static functions exposed by theCActiveScheduler class which all follow this pattern.168ACTIVE OBJECTSThe relevant part of the class definition is as follows:class CActiveScheduler : public CBase{public:IMPORT_C CActiveScheduler();IMPORT_C ∼CActiveScheduler();IMPORT_C static void Install(CActiveScheduler* aScheduler);IMPORT_C static CActiveScheduler* Current();IMPORT_C static void Add(CActive* aActive);IMPORT_C static void Start();IMPORT_C static void Stop();private:TPriQue<CActive> iActiveQ;};The scheduler implements an ordered queue of CActive objects. Theordering is based upon the priority that you assign to the active objectwhen it is constructed. Higher-priority objects are inserted near the start,or head, of the queue.The kernel also provides all user threads with a request semaphore.This is a standard counting semaphore object that is incremented by thekernel each time a request in that thread has been completed.Each active object is associated with just one object that has requestfunctions – functions that take a TRequestStatus& parameter.
Whena program calls an active object’s request function (SetHello() in theexample we saw earlier), the active object passes the request on to theasynchronous service provider. It passes its own iStatus member asthe TRequestStatus& parameter to the request function and, havingcalled the request function, it immediately calls SetActive().The TRequestStatus is a relatively simple class that encapsulatesa completion code and a series of binary flags that describe the stateof a request. Valid binary bit flags for the request are ERequestPending, for a request that is issued but has not yet been completed, andEActive, which is closely related to the CActive::IsActive() andCActive::SetActive() functions, meaning that the object is able toaccept events.Here is the class definition of TRequestStatus from e32cmn.h:class TRequestStatus{public:inline TRequestStatus();inline TRequestStatus(TInt aVal);inline TInt operator=(TInt aVal);inline TBool operator==(TInt aVal) const;inline TBool operator!=(TInt aVal) const;inline TBool operator>=(TInt aVal) const;inline TBool operator<=(TInt aVal) const;inline TBool operator>(TInt aVal) const;HOW IT WORKS169inline TBool operator<(TInt aVal) const;inline TInt Int() const;private:enum{EActive= 1, //bit0ERequestPending= 2, //bit1};TInt iStatus;TUint iFlags;friend class CActive;friend class CActiveScheduler;friend class CServer2;};The TRequestStatus class features a range of operator overloads to enable value comparison against the request completion code,iStatus.
Additionally, it has several friends, including CActive andCActiveScheduler, permitting direct access to the iStatus andiFlags member variables by these objects.Before starting to service a request function, the asynchronous serviceprovider sets the value of TRequestStatus to KRequestPending(which is defined as -KMaxTInt). This is required of all asynchronousservice providers. The vast majority of such entities are servers andtherefore the Symbian OS client–server framework ensures that whenan asynchronous request is issued to a server, the request status isautomatically set to pending. This happens on the client-side, prior to theserver receiving the client’s request.The TRequestStatus assignment operator ensures that when a newvalue is assigned to a request status, the flags are updated accordingly:inline TInt TRequestStatus::operator=(TInt aVal){if(aVal == KRequestPending)iFlags|=TRequestStatus::ERequestPending;elseiFlags&=∼TRequestStatus::ERequestPending;return (iStatus=aVal);}If the value is equal to KRequestPending then the ERequestPending flag is set within the request status, otherwise it is cleared.
Inthis way, the request object’s state is always kept up-to-date which, aswe shall see, allows the framework to use this information during theRunL() dispatch phase.As we saw in our earlier example, after an asynchronous request hasbeen issued, the active object should always immediately indicate that it isactive by calling SetActive(). The implementation of SetActive()is as follows:170ACTIVE OBJECTSEXPORT_C void CActive::SetActive(){__ASSERT_ALWAYS(!(iStatus.iFlags&TRequestStatus::EActive),Panic(EReqAlreadyActive));__ASSERT_ALWAYS(IsAdded(),Panic(EActiveNotAdded));iStatus.iFlags|=TRequestStatus::EActive;}The function first checks that the object’s TRequestSta tus flags donot already indicate that the object is active.
This is a programming errorthat would result in a request being issued to the service provider whilstan existing request is already pending or awaiting RunL() processing.Secondly, an additional check is made to ensure that the active objecthas added itself to the active scheduler. If the object has not been addedto the active scheduler then the scheduler will not be able to locatethe object when the request is eventually completed. Finally, becauseCActive is a friend of TRequestStatus, CActive is permitted toaccess the TRequestStatus::iFlags member directly, which it doesin order to set the EActive bit.
This is a hint to the framework so that itknows that the object is now ready to accept an event.When the asynchronous service provider finishes processing therequest, it generates an event. This means that it signals the thread’srequest semaphore and posts a completion code (such as KErrNoneor any other standard error code – anything except KRequestPendingis permissible) into TRequestStatus.
The act of completing an asynchronous request increments the requesting thread’s request semaphorevalue by one.The active scheduler is responsible for detecting the occurrence ofan event, which it then associates with the active object that originallyrequested it, and calls RunL() on that active object.The active scheduler calls User::WaitForAnyRequest() to detectan event. This function’s behavior is linked to the current counter valuefor the thread’s request semaphore. The counter value is decremented byone and, if the resultant request semaphore value is zero or above, thenthe call returns immediately, indicating that an event has occurred. If thesemaphore counter value drops below zero as a result of decrementingthe value by one, then the thread is suspended until an event occurs andthe request semaphore is signaled to indicate an event completion.When the thread is resumed as a result of an asynchronous requestbeing completed, the active scheduler then scans through all its activeobjects searching for the object that should handle the event.
The criteriathe active scheduler applies to locating the correct active object are fairlystraightforward.• The scheduler starts at the beginning of its active object queue, lookingat the highest-priority active object in the entire active scheduler.ACTIVE OBJECT PRIORITIES171◦ It checks to see if the object is active by inspecting its request statusflags. As we saw earlier, the flags are updated when SetActive()is called.◦ If the object is active and its iStatus value no longer equalsKRequestPending, then this implies that the object made anasynchronous request that has now been completed by the serviceprovider. If the object is active and the iStatus value is stillKRequestPending, then it would imply that the object hasmade an asynchronous request that has not yet been completed.◦ If the object is not active then the next object in the queue isselected and the same checks are applied once more.• Having located the active object that should handle the event, theactive scheduler then clears its request status flags (CActive::iStatus.iFlags) to indicate that the object is no longer active orhas any request pending.