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

120

EVENT-DRIVEN MULTITASKING USING ACTIVE OBJECTS

resources owned by the object, including any handle to the asynchronous service provider.

The CActive base class destructor is virtual and its implementation checks that the active object is not currently active. It panics if any request is outstanding, that is, if Cancel() has not been called. This catches any programming errors which could lead to the situation where a request completes after the active object to handle it has been destroyed. This would otherwise result in a ”stray signal”, described further in Chapter 9, where the active scheduler cannot locate an active object to handle the event.

Having verified that the active object has no issued requests outstanding, the CActive destructor removes the active object from the active scheduler.

The destructor of a CActive-derived class should always call Cancel() to terminate any outstanding requests before cleanup proceeds.

8.4 Example Code

The example below illustrates the use of an active object class to wrap an asynchronous service, in this case a timer provided by the RTimer service. In fact, Symbian OS already supplies an abstract active object class, CTimer, which wraps RTimer and can be derived from, specifying the action required when the timer expires. However, I’ve created a new class, CExampleTimer, because it’s a straightforward way of illustrating active objects. Figure 8.2 illustrates the classes involved and their relationship with the active scheduler.

When the timer expires, the RunL() event handler checks the active object’s iStatus result and leaves if it contains a value other than KErrNone so the RunError() method can handle the problem. In this case, the error handling is very simple: the error returned from the request is logged to file. This could have been performed in the RunL() method, but I’ve separated it into RunError() to illustrate how to use the active object framework to split error handling from the main logic of the event handler. If no error occurred, the RunL() event handler logs the timer completion to debug output using RDebug::Print() (described in Chapter 17) and resubmits the timer request with the stored interval value. In effect, once the timer request has started, it continues to expire and be resubmitted until it is stopped by a call to the Cancel() method on the active object.

 

 

EXAMPLE CODE

121

 

 

 

 

 

 

 

 

 

 

 

 

CActive

 

 

 

 

 

 

CActiveScheduler

 

 

 

 

 

 

 

 

 

 

 

 

RunL()

 

 

n

1

 

Add()

 

DoCancel()

 

 

 

 

 

 

 

 

 

 

RunError()

 

 

 

 

 

...

 

 

...

 

 

 

 

 

 

 

 

 

iActive

 

 

 

 

 

 

 

 

 

iStatus

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

CExampleTimer

NewL() After RunL() DoCancel()

RunError()

...

iTimer

RTimer

Figure 8.2 CExampleTimer and its relationship with RTimer, CActive and CActiveScheduler

_LIT(KExampleTimerPanic, "CExampleTimer");

class CExampleTimer : public CActive

{

public:

CExampleTimer();

static CExampleTimer* NewL();

void After(TTimeIntervalMicroSeconds32& aInterval);

protected:

 

CExampleTimer();

 

void ConstructL();

 

protected:

 

virtual void RunL();

// Inherited from CActive

virtual void DoCancel();

virtual TInt RunError(TInt aError); private:

RTimer iTimer; TTimeIntervalMicroSeconds32 iInterval; };

CExampleTimer::CExampleTimer()

: CActive(EPriorityStandard) { CActiveScheduler::Add(this); }

void CExampleTimer::ConstructL()

122

EVENT-DRIVEN MULTITASKING USING ACTIVE OBJECTS

{// Create the asynchronous service provider User::LeaveIfError(iTimer.CreateLocal());

}

CExampleTimer* CExampleTimer::NewL()

{...} // Code omitted for clarity

CExampleTimer:: CExampleTimer()

{

Cancel();

iTimer.Close();

}

void CExampleTimer::After(TTimeIntervalMicroSeconds32& aInterval) {// Only allow one timer request to be submitted at a time

// Caller must call Cancel() before submitting another __ASSERT_ALWAYS(!IsActive(), User::Panic(KExampleTimerPanic,

KErrInUse)); iInterval = aInterval;

iTimer.After(iStatus,aInterval); // Set the RTimer SetActive(); // Mark this object active

}

void CExampleTimer::RunL()

{// If an error occurred (admittedly unlikely) // deal with the problem in RunError()

User::LeaveIfError(iStatus.Int());

// Otherwise, log the timer completion and resubmit the timer _LIT(KTimerExpired, "Timer Expired\n"); RDebug::Print(KTimerExpired); // See Chapter 17 for more details iTimer.After(iStatus, iInterval);

SetActive();

}

void CExampleTimer::DoCancel() {// Cancel the timer iTimer.Cancel();

}

TInt CExampleTimer::RunError(TInt aError)

{// Called if RunL() leaves, aError contains the leave code _LIT(KErrorLog, "Timer error %d"); RDebug::Print(KErrorLog, aError); // Logs the error

return (KErrNone);

// Error has been handled

}

 

The example is simplistic but it demonstrates the use of an active object to wrap an asynchronous function, in this case, timer completion. Here’s how a client of class CExampleTimer can expect to use the active object, which it stores as a member variable called iExampleTimer, illustrating the transparency of active object classes from a client’s perspective:

// Class CClient has a member variable CExampleTimer* iExampleTimer

void CClient::StartRepeatingTimerL()

{

iExampleTimer = CExampleTimer::NewL();

APPLICATION CODE AND ACTIVE OBJECTS

123

iExampleTimer->After(1000000);

}

void CClient::StopRepeatingTimer()

{

iExampleTimer->Cancel(); delete iExampleTimer;

iExampleTimer = NULL; // Prevents re-use or double deletion

}

8.5 Threads Without an Active Scheduler

Most threads running on Symbian OS have an active scheduler, which is usually created implicitly by a framework (e.g. CONE for the GUI framework). However, if you are implementing a server, you have to create and start an active scheduler explicitly before you can use active objects. Likewise, console-based test code may not use active objects directly itself, but must create an active scheduler in its main thread if it depends on components which do use active objects.

There are a few threads in Symbian OS which intentionally do not have an active scheduler and thus cannot use active objects or components that use them:

The Java implementation does not support an active scheduler and native Java methods may not use active objects. It is permissible to make calls in code to C++ servers which do use them, since these run in a separate thread which has a supporting active scheduler.

The C Standard Library, STDLIB, thread has no active scheduler and thus standard library code cannot use active objects. Functions provided by the Standard Library may however be used in active object code, for example in an initialization or a RunL() method. The functions should be synchronous and return quickly, as required by all active object implementations.

OPL does not provide an active scheduler and C++ extensions to OPL (OPXs) must not use active objects or any component which uses them.

8.6Application Code and Active Objects

The active object model is very easy to use without needing a full understanding of how it works. In this chapter, I’ve described how to handle events resulting from the completion of asynchronous functions on Symbian OS. This involves defining CActive-derived classes, and providing