Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Symbian OS Explained - Effective C++ Programming For Smartphones (2005) [eng].pdf
Скачиваний:
60
Добавлен:
16.08.2013
Размер:
2.62 Mб
Скачать

RESPONSIBILITIES OF AN ACTIVE OBJECT

131

An active object’s priority is only an indication of the order in which the active scheduler performs lookup and event-handling. It does not reflect an ability to pre-empt other active objects.

9.2 Responsibilities of an Active Object

Figure 9.1 illustrates the roles and actions of the active scheduler, active object and the asynchronous service provider. It extends Figure 8.1.

Executable

Active Object

Active Scheduler

Asynchronous Service

Provider

 

 

 

Create and Install the

Active Scheduler

Create Active Object (with appropriate priority) and add it to the Active Scheduler

Issue a request to the

 

 

Make a request to the Asynchronous

 

Sets iStatus=KRequestPending

Active Object

 

 

Service Provider, passing in iStatus

 

 

and starts the service

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Call SetActive()

 

 

 

 

Start the Active

 

 

 

 

 

 

Wait Loop

 

Scheduler if it is not

CActiveScheduler::Start()

 

 

 

 

already started

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Service completes and uses

 

 

 

RunL() handles the

Calls RunL() on the Active

RequestComplete() to

 

 

 

completed event

notify the Active Scheduler

 

 

 

Object with

 

 

 

 

and resubmits

 

(by signalling its thread

 

 

 

iStatus!=KRequestPen

 

 

 

another request or

semaphore) and to post a

 

 

 

ding and iActive=ETrue

 

 

 

stops the Active

completion result

 

 

 

 

 

 

 

 

 

Scheduler

 

 

 

 

CActiveScheduler::Stop()

PROCESS OR

THREAD

BOUNDARY

Figure 9.1 Roles and actions of the active scheduler, an active object and an asynchronous service provider

132

ACTIVE OBJECTS UNDER THE HOOD

It looks complex, but I’ll explain how it all fits together throughout this chapter and you’ll probably want to refer back to it later.

The following list summarizes the responsibilities of an active object:

As I described in Chapter 8, the priority of an active object must be set on construction. The priority generally defaults to EPriorityStandard (=0, from class CActive) or EActivePriorityDefault (=0 if using the TActivePriority enumeration defined in coemain.h for use with application code). This is the standard priority for an active object and should be used unless there is a good reason to set its priority to some other value, for example to EActivePriorityWsEvents (=100) for handling user input responsively.

An active object provides at least one method for clients to initiate requests to its encapsulated asynchronous service provider. The active object always passes its own iStatus object to the asynchronous function, so does not need to include a TRequestStatus reference among the parameters to the request issuer method unless it is acting as a secondary provider of asynchronous services.

After submitting a request to an asynchronous service provider, the active object must call SetActive() upon itself. This sets the iActive flag, which indicates an outstanding request. This flag is used by the active scheduler upon receipt of an event and by the base class upon destruction, to determine whether the active object can be removed from the active scheduler.

An active object should only submit one asynchronous request at a time. The active scheduler has no way of managing event-handling for multiple requests associated with one active object.

An active object should pass its iStatus object to an asynchronous service function. It should not reuse that object until the asynchronous function has completed and been handled. The active scheduler inspects the TRequestStatus of each active object to determine its completion state and the event-handling code uses the value it contains to ascertain the completion result of the function.

An active object must implement the pure virtual methods RunL() and DoCancel() declared in the CActive base class. Neither method should perform lengthy or complex processing, to avoid holding up event handling in the entire thread. This is particularly important in GUI applications where all user interface code runs in the same thread. If any single RunL() is particularly lengthy, the user interface will be unresponsive to input and will ”freeze” until that event handler has completed.

RESPONSIBILITIES OF AN ASYNCHRONOUS SERVICE PROVIDER

133

An active object must ensure that it is not awaiting completion of a pending request when it is about to be destroyed. As destruction removes it from the active scheduler, later completion will generate an event for which there is no associated active object. To prevent this, Cancel() should be called in the destructor of an active object. The destructor of the CActive base class checks that there is no outstanding request before removing the object from the active scheduler and raises an E32USER–CBASE 40 panic if there is, to highlight the programming error. The base class destructor cannot call Cancel() itself because that method calls the derived class implementation of DoCancel() – and, of course, C++ dictates that the derived class has already been destroyed by the time the base class destructor is called.

Objects passed to the asynchronous service provider by the issuer methods must have a lifetime at least equal to the time taken to complete the request. This makes sense when you consider that the provider may use those objects until it is ready to complete the request, say if it is retrieving and writing data to a supplied buffer. This requirement means that parameters supplied to the provider should usually be allocated on the heap (very rarely, they may be on a stack frame that exists for the lifetime of the entire program). In general, parameters passed to the asynchronous service provider should belong to the active object, which is guaranteed to exist while the request is outstanding.

If a leave can occur in RunL(), the class should override the default implementation of the virtual RunError() method to handle it. RunError() was added to CActive in Symbian OS v6.0 to handle any leaves that occur in the RunL() event handler. If a leave occurs, the active scheduler calls the RunError() method of the active object, passing in the leave code. RunError() should return KErrNone to indicate that it has handled the leave, say by cleaning up or resetting the state of the active object. The default implementation, CActive::RunError(), does not handle leaves and indicates this by simply returning the leave code passed in.

9.3Responsibilities of an Asynchronous Service Provider

An asynchronous service provider has the following responsibilities:

Before beginning to process each request, the provider must set the incoming TRequestStatus value to KRequestPending to indicate to the active scheduler that a request is ongoing.

134

ACTIVE OBJECTS UNDER THE HOOD

When the request is complete, the provider must set the TRequestStatus value to a result code other than KRequestPending by calling the appropriate RequestComplete() method from the

RThread or User class.

The asynchronous service provider must only call RequestComplete() once for each request. This method generates an event in the requesting thread to notify it of completion. Multiple completion events on a single active object result in a stray signal panic. Completion may occur normally, because of an error condition or because the client has cancelled an outstanding request. If the client calls Cancel() on a request after it has completed, the asynchronous service provider must not complete it again and should simply ignore the cancellation request. This is discussed further in Sections 9.8 and 9.9.

The provider must supply a corresponding cancellation method for each asynchronous request; this should complete an outstanding request immediately, posting KErrCancel into the TRequestStatus object associated with the initial request.

9.4Responsibilities of the Active Scheduler

The active scheduler has the following responsibilities:

Suspending the thread by a call to User::WaitForAnyRequest(). When an event is generated, it resumes the thread and inspects the list of active objects to determine which has issued the request that has completed and should be handled.

Ensuring that each request is handled only once. The active scheduler should reset the iActive flag of an active object before calling its handler method. This allows the active object to issue a new request from its RunL() event handler, which results in SetActive() being called (which would panic if the active object was still marked active from the previous request).

Placing a TRAP harness around RunL() calls to catch any leaves occurring in the event-handling code. If the RunL() call leaves, the active scheduler calls RunError() on the active object initially. If the leave is not handled there, it passes the leave code to CActiveScheduler::Error(), described in more detail shortly.

Raising a panic (E32USER–CBASE 46) if it receives a ”stray signal”. This occurs when the request semaphore has been notified of an event, but the active scheduler cannot find a ”suitable” active object