×
Namespaces

Variants
Actions

Timer issues and tips using Symbian C++

From Nokia Developer Wiki
Jump to: navigation, search

Archived.pngArchived: This article is archived because it is not considered relevant for third-party developers creating commercial solutions today. If you think this article is still relevant, let us know by adding the template {{ReviewForRemovalFromArchive|user=~~~~|write your reason here}}.

The article is believed to be still valid for the original topic scope.

Article Metadata
Article
Created: User:Technical writer 1 (14 Nov 2007)
Last edited: lpvalente (11 Jun 2014)


Contents

Introduction to timers

Timing services provide both synchronous and asynchronous timers:

• User::After: synchronous, suspends the current thread for the given period.

• User::At: synchronous, suspends the current thread until the given time.

• RTimer is a kernel object that provides an asynchronous timing service, and is a base for asynchronous timer implementations.

• CPeriodic uses RTimer::After to implement a periodic timer.

• CHeartBeat uses RTimer::Lock to implement a fraction of a second timer capable of detecting and informing lost heartbeats.

• CDeltaTimer uses RTimer::After to implement a queue of variable length timer intervals.

• CTimer is an active object wrapping RTimer, and is a base for timer active objects. You will need to inherit from this and implement CTimer::RunL to use CTimer.

Accuracy of timers on EPOC Kernel Architecture 1 (EKA1) is 1/64th of a second, but on the emulator only 1/10th of a second. On EPOC Kernel Architecture 2 (EKA2), Symbian OS v8.1b and above, the timer accuracy is 1/64th of a second on both, emulator and device. EKA2 also provides higher resolution (one ms) timers via RTimer::HighRes() and User::AfterHighRes(). Timer accuracy can be checked with HAL::Get(HALData::ESystemTickPeriod, tickPeriod).

Note also that the timer events are affected by other running applications — that is, the current load of the system. Your application will not receive the timer events immediately, because it requires the kernel to switch execution to your application's thread, and there may be other threads to be served before yours; thus you will not be able to handle the event immediately.

Currently most devices do not support the turn-on feature of timers, where the device would be powered on when using RTimer::At or User::At — however, some devices can wake up from a suspended state.

Use the timers only when absolutely needed — polling certain things in Symbian OS is rarely needed, because most of the APIs provide callback mechanisms to inform about changes. For example, notification of thread termination can be received by using RThread::Logon, and notification of changes in files and directories can be received by using RFs::NotifyChange.

Note that applications/servers are closed when shutting down the device, so the timers cannot fire an event until the application/server is started and the timer is restarted. Therefore, to proceed with the timer after device reboot, it is usually better simply to check the system time on application/server startup.


Issues and tips

At-timer issues

Interval timers, such as RTimer::After, are relatively easy to implement, but there are issues worth noting when implementing stop watch timers using RTimer::At or User::At. When the system time changes, the timer will complete immediately with the result KErrAbort. This is logical, because the At-timers are set to stop on a specific date and time — the timer cannot be kept running anymore because the set time may no longer be valid or desirable, so the timer passes the decision making to whoever started the timer. System time changes do not affect interval timers, because the timer can still complete on time, [n] microseconds from this time onwards. With interval timers the changes in system time are not usually an issue, unless you are expecting the interval timer to complete on a specific date and time — which would be a poor design because the system time may change while the timer is running.

The system time can change when, for example, automatic time and date update from the network is enabled and a new time is received from network, or when changing time zone or the daylight saving setting. Also, the system time can be changed by any application at any time by using User::SetHomeTime(). In all of these cases all the active At-timers will complete immediately with KErrAbort.

The decision about what to do when a timer completes with KErrAbort depends on the application. You could either restart the timer with the same time, or try to determine how the time has changed and recalculate a new stop time for the timer — or give up and do not start the timer again.

For example; you are implementing an application that would have to make a backup of a database every day at 21:00 (or some other time, time defined by user). You cannot (easily) use interval timers here, because the timer would expire at the wrong time if the system time changes. Using stop watch timers (At-timers), you will need to handle system time changes. This case seems to be a simple one because recalculating a new stop time is possible and even easy — just restart the stop watch timer with the current date at 21:00, or if that time has already passed, set the timer to stop on the next day at 21:00.

Also remember that an At-timer may complete with KErrOverFlow to indicate that the requested completion time is too far in the future (what is "too far" depends on the platform), or KErrUnderflow, if the set time is in the past. These errors do not occur when the system time changes. They are only possible if the timer was started with an invalid completion time, so you can prevent these errors before even starting the timer.

Deleting CDeltaTimer causes panic on exit

On Symbian OS versions earlier than v8.0a, the CDeltaTimer's destructor does not call RTimer::Close(), resulting in a CONE 36 panic when exiting the application (open handles were found during application shutdown).

This problem can be replicated with simple code:

CDeltaTimer *timer = CDeltaTimer::NewL(0);

delete timer; // ...and exit the application

There are no simple fixes for this; luckily the CDeltaTimer is rarely needed, and the panic is only thrown on debug builds. You could overcome this issue by closing CDeltaTimer's RTimer in your own code (the timer is a protected member, and you would need to access the RTimer member by its offset, or inherit a class from CDeltaTimer, to do so), but this is certainly not recommended because it may not be compatible with newer platform versions where this issue is already fixed.

Using RTimer to get notifications of system time changes

As the At-timers terminate with KErrAbort when the system time changes, the timer can be used to monitor changes in system time. Just start a timer, and catch KErrAbort errors, for example:

void CSystemTimeMonitor::Start()
 {
 Cancel(); 
 TTime now; 
 now.HomeTime();
 iTimer.At(iStatus, now + TTimeIntervalYears(1));
 SetActive();
 }
 void CSystemTimeMonitor::RunL() 
 {
 switch(iStatus.Int()) 
 {
 case KErrAbort:
 iObserver.SystemTimeChanged();
 // Flowthrough
 case KErrNone:
 // keep the timer running;
 Start();
 break;
 case KErrCancel:
 break;
 default:
 // notify errors to observer
 iObserver.TimerStopped(iStatus.Int());
 break;
 }
 }

WaitForRequest with timeout

It is sometimes needed to wait for completion of a specific operation synchronously, but only for a period of time to prevent blocking execution indefinitely. Symbian OS does not provide a ready-made function for this, but RTimer can be easily used to create the timeout functionality. For example,

void CMyClass::WaitForRequestWithTimeoutL(TRequestStatus& aStatus,
 TTimeIntervalMicroseconds32 aTimeOut)
 {
 TRequestStatus waitStatus = KRequestPending;
 RTimer timer;
 User::LeaveIfError(timer.CreateLocal());
 CleanupClosePushL(timer);
 timer.After(waitStatus, aTimeOut);
 // Wait for two requests – if timer completes first, we have a
 // timeout.
 User::WaitForRequest(aStatus, waitStatus);
 if(waitStatus.Int() != KRequestPending)
 User::LeaveIfError(waitStatus.Int());
 timer.Cancel();
 if(aStatus.Int() == KRequestPending)
 {
 // Operation did not complete in time.
 User::Leave(KErrTimedOut);
 // Note that the started operation may complete later, so
 // handle this case properly – for example cancel the timedout
 // operation.
 }
 CleanupStack::PopAndDestroy(); // timer
 }
This page was last modified on 11 June 2014, at 20:14.
62 page views in the last 30 days.

Was this page helpful?

Your feedback about this content is important. Let us know what you think.

 

Thank you!

We appreciate your feedback.

×