examples/Base/Timers/Periodic/Periodic.cpp

00001 // Copyright (c) 2000-2009 Nokia Corporation and/or its subsidiary(-ies).
00002 // All rights reserved.
00003 // This component and the accompanying materials are made available
00004 // under the terms of "Eclipse Public License v1.0"
00005 // which accompanies this distribution, and is available
00006 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
00007 //
00008 // Initial Contributors:
00009 // Nokia Corporation - initial contribution.
00010 //
00011 // Contributors:
00012 //
00013 // Description:
00014 // Shows difference between CPeriodic and CHeartBeat
00015 // CPeriodic uses repeated RTimer::After() requests to get
00016 // a periodic tick - simple, but the time the tick is serviced lags
00017 // increasingly.
00018 // As a nuance of CPeriodic, it takes a TCallBack to service
00019 // its requests.
00020 // CHeartbeat uses repeated RTimer::Lock() requests: its
00021 // requests complete in synchronization with the beating
00022 // of the system clock.
00023 // As a nuance of CHeartbeat, it takes an MBeating* mixin:
00024 // the MBeating's Beat() function is called when completion
00025 // occurs in sync, and Synchronize() is called when
00026 // synchronization has been lost.
00027 //
00028 
00029 
00030 
00031 // standard example header
00032 #include "CommonFramework.h"
00033 
00034 // beginning of real example
00035 
00036 #include <e32math.h>
00037 
00038 void RandomDelay(TInt64& aSeed, TInt /*aTimerNumber*/)
00039         {
00040         // initialize seed first time through
00041         if (aSeed==0)
00042                 {
00043                 TTime time;
00044                 time.HomeTime();
00045                 aSeed=time.Int64();
00046                 }
00047         // ok, here we go
00048         TReal randomZeroToOne;
00049         randomZeroToOne = Math::FRand(aSeed);
00050         TReal realDelay;
00051         realDelay = randomZeroToOne * randomZeroToOne * 2000000;
00052         TInt32 intDelay;
00053         Math::Int(intDelay, realDelay);
00054         TTimeIntervalMicroSeconds32 delayMicroSeconds;
00055         delayMicroSeconds=intDelay;
00056         User::After(delayMicroSeconds);
00057         }
00058 
00059 // A version of the RandomDelay function which is not random ! 
00060 // The delay is fixed at 1000000us and may be useful to 
00061 // experiment with. 
00062 //
00063 //
00064 //void RandomDelay(TInt64& /* aSeed */, TInt aTimerNumber)
00065 //      {
00066 //      User::After(1000000);
00067 //  _LIT(KFormatMisc,"Delay for timer %d: 1000000us\n");
00068 //      console->Printf(KFormatMisc, aTimerNumber);
00069 //      }
00070 
00071 
00072 class TAppRunner
00073         {
00074 public:
00075         TAppRunner();
00076         void NotifyFinished(); // notify an active object has finished
00077         void NotifyStarted(); // notify an active object has started
00078 // private:
00079         TInt iActiveObjects; // count of active objects
00080         };
00081 
00082 TAppRunner::TAppRunner()
00083         {
00084         iActiveObjects=0;
00085         }
00086 
00087 void TAppRunner::NotifyStarted()
00088         {
00089         iActiveObjects++;
00090         }
00091 
00092 void TAppRunner::NotifyFinished()
00093         {
00094         iActiveObjects--;
00095         if (iActiveObjects==0) CActiveScheduler::Stop();
00096         }
00097 
00098 /*
00099         CPeriodicRunner class
00100 
00101         Constructor makes a CPeriodic and sets it off with one-second ticks.
00102         These are fielded by the callback function, the static Tick(TAny*),
00103         which simply casts the pointer to a CPeriodicRunner* and calls
00104         the non-static callback, DoTick().
00105 
00106         Processing gets behind: when the ticks left have counted down to zero,
00107         it should be behind by a second or two.  The destructor indicates how many
00108         seconds since the object's creation.
00109 */
00110 
00111 class CPeriodicRunner : public CBase
00112         {
00113 public:
00114         // construct, add CPeriodic to active scheduler, and start it
00115         static CPeriodicRunner* NewL(TInt aTickInterval, TInt aTotalTicks, TAppRunner& aAppRunner);
00116         ~CPeriodicRunner(); // destruct and give statistics
00117 protected:
00118         CPeriodicRunner(TInt aTickInterval, TInt aTotalTicks, TAppRunner& aAppRunner);
00119 private:
00120         void ConstructL(); // second construction phase
00121         // functions for TCallBack protocol
00122         static TInt Tick(TAny* aObject); // directly called
00123         void DoTick(); // indirectly called
00124 private:
00125       // constructor parameters
00126         TAppRunner& iAppRunner; // notify when started and finished
00127         TInt iTotalTicks;       // total number of ticks requested
00128         TInt iTickInterval;     // the tick interval in microseconds
00129         
00130           // things set up by ConstructL()
00131         TTime iStartTime;       // when we were started
00132         CPeriodic* iPeriodic;   // periodic timer active object
00133         
00134           // remaining ticks will be decremented as we go
00135         TInt iTicksLeft;        // number of ticks before we expire
00136         TInt iTimerNumber;      // indentifying number for the timer
00137         
00138         // seed for random delay generator
00139         TInt64 iDelaySeed;
00140         };
00141 
00142 // protected C++ constructor
00143 CPeriodicRunner::CPeriodicRunner(TInt aTickInterval, TInt aTotalTicks, TAppRunner& aAppRunner)
00144         : iAppRunner(aAppRunner), iTotalTicks(aTotalTicks), iTickInterval(aTickInterval)
00145         {}
00146 
00147 // private second-phase constructor
00148 void CPeriodicRunner::ConstructL()
00149         {
00150         iStartTime.HomeTime();
00151         iPeriodic =CPeriodic::NewL(0); // neutral priority
00152         iAppRunner.NotifyStarted();
00153         iTimerNumber = iAppRunner.iActiveObjects; // set idenfifying number for timer
00154         iTicksLeft = iTotalTicks;
00155         // variable (actually 1 second) delay and interval 
00156         iPeriodic->Start(iTickInterval,iTickInterval,TCallBack(Tick, this));
00157         }
00158 
00159 // construct, add CPeriodic to active scheduler, and start it
00160 CPeriodicRunner* CPeriodicRunner::NewL(TInt aTickInterval, TInt aTotalTicks, TAppRunner& aAppRunner)
00161         {
00162         CPeriodicRunner* self=new (ELeave) CPeriodicRunner(aTickInterval, aTotalTicks, aAppRunner);
00163         CleanupStack::PushL(self);
00164     self->ConstructL();
00165         CleanupStack::Pop();
00166     return self;
00167         }
00168 
00169 // destruct and give statistics
00170 CPeriodicRunner::~CPeriodicRunner()
00171         {
00172         TTimeIntervalMicroSeconds elapsedTime;
00173         TTime currentTime;
00174         // set current time
00175         currentTime.HomeTime(); // set currentTime to now
00176         // find and show elapsed time & ticks 
00177         elapsedTime = currentTime.MicroSecondsFrom(iStartTime);
00178         
00179         _LIT(KFormat1,"Periodic timer %d finished after: %Ld microseconds for %d %dus ticks\n");
00180         console->Printf(KFormat1,iTimerNumber,elapsedTime.Int64(),iTotalTicks,iTickInterval);
00181 
00182         
00183         
00184         //  cancel any outstanding request; delete owned CPeriodic object 
00185         iPeriodic->Cancel();
00186     delete iPeriodic;
00187         // tell app runner we've finished (if we're last, scheduler will stop)
00188         iAppRunner.NotifyFinished();
00189         }
00190 
00191 // private
00192 TInt CPeriodicRunner::Tick(TAny* aObject)
00193         {
00194         ((CPeriodicRunner*)aObject)->DoTick(); // cast, and call non-static function
00195         return 1;
00196         }
00197 
00198 // private
00199 void CPeriodicRunner::DoTick()
00200         {
00201         iTicksLeft--;
00202         _LIT(KFormat2,"Periodic timer %d: %d ticks done\n");
00203         console->Printf(KFormat2, iTimerNumber, iTotalTicks - iTicksLeft);
00204         if(iTicksLeft==0)
00205                 {
00206                 delete this;
00207                 }
00208         RandomDelay(iDelaySeed,iTimerNumber); // a random delay to mess up the timing
00209         }
00210 
00211 /*
00212         CHeartbeatRunner class
00213 
00214         This class receives beats in sync with the system clock.  It also has a much
00215         nicer interface than for periodic timers - the MBeating mixin, which is nicely
00216         object-oriented.
00217 
00218         Most of the time, the Beat() function is called which trivially updates the tick
00219         count.  Occasionally, synchronization is lost, and the Synchronize() function
00220         is called instead: this must find out from the system time how many ticks should
00221         have been counted, and update things accordingly.
00222 
00223         The destructor gives the same comparisons as the CPeriodic's.  The discrepancy
00224         between the number of ticks and the number of seconds since construction should
00225         never be more than is accounted for by the last heartbeat.
00226 */
00227 
00228 class CHeartbeatRunner : public CBase, public MBeating
00229         {
00230 public:
00231         // construct, add CHeartbeat to active scheduler, and start it
00232         static CHeartbeatRunner* NewL(TInt aTickInterval, TInt aTotalTicks, TAppRunner& aAppRunner);
00233         ~CHeartbeatRunner(); // destruct and give statistics
00234 protected:
00235         CHeartbeatRunner(TInt aTickInterval, TInt aTotalTicks, TAppRunner& aAppRunner);
00236 private:
00237         void ConstructL();
00238         // functions for MBeating protocol
00239         void Beat(); // called when beat works ok
00240         void Synchronize(); // called when we need to synchronize
00241 private:
00242         // constructor parameters
00243         TAppRunner& iAppRunner; // notify when started and finished
00244         TInt iTotalTicks; // number of ticks requested
00245         TInt iTickInterval; // tick length in microseconds
00246         // things set up by ConstructL
00247         TTime iStartTime; // when we were started
00248         CHeartbeat* iHeartbeat; // heartbeat active object
00249         // ticks left decrements as we go
00250         TInt iTicksLeft; // number of ticks before we expire
00251         TInt iTimerNumber; // indentifying number for the timer
00252         // seed for random delay generator
00253         TInt64 iDelaySeed;
00254         };
00255 
00256 // protected C++ constructor
00257 CHeartbeatRunner::CHeartbeatRunner(TInt aTickInterval, TInt aTotalTicks, TAppRunner& aAppRunner)
00258         :iAppRunner(aAppRunner), iTotalTicks(aTotalTicks), iTickInterval(aTickInterval)
00259         {}
00260 
00261 // private second-phase constructor
00262 void CHeartbeatRunner::ConstructL()
00263         {
00264         iStartTime.HomeTime();
00265         iHeartbeat=CHeartbeat::NewL(0); // neutral priority
00266         iAppRunner.NotifyStarted();
00267         iTimerNumber = iAppRunner.iActiveObjects; // set idenfifying number for timer
00268         iTicksLeft = iTotalTicks;
00269         // start the heartbeat timer, beating exactly on the second
00270         iHeartbeat->Start(ETwelveOClock,this);
00271         }
00272 
00273 // construct, add CHeartbeat to active scheduler, and start it
00274 CHeartbeatRunner* CHeartbeatRunner::NewL(TInt aTickInterval, TInt aTotalTicks, TAppRunner& aAppRunner)
00275         {
00276         CHeartbeatRunner* self=new (ELeave) CHeartbeatRunner(aTickInterval, aTotalTicks, aAppRunner);
00277         CleanupStack::PushL(self);
00278     self->ConstructL();
00279         CleanupStack::Pop();
00280     return self;
00281         }
00282 
00283 // destruct and give statistics
00284 CHeartbeatRunner::~CHeartbeatRunner()
00285         {
00286         TTimeIntervalMicroSeconds elapsedTime;
00287         TTime currentTime;
00288         currentTime.HomeTime(); // set current time to now
00289         // find and show elapsed time & ticks 
00290         elapsedTime = currentTime.MicroSecondsFrom(iStartTime); 
00291         
00292         _LIT(KFormat3,"Heartbeat timer %d finished after: %Ld microseonds for %d %dus ticks\n");
00293         console->Printf(KFormat3,iTimerNumber,elapsedTime.Int64(),iTotalTicks,iTickInterval);
00294         
00295         //  cancel any outstanding request; delete owned CPeriodic object 
00296         iHeartbeat->Cancel();
00297     delete iHeartbeat;
00298         // tell app runner we've finished (if last, scheduler will stop)
00299         iAppRunner.NotifyFinished();
00300         }
00301 
00302 // private
00303 void CHeartbeatRunner::Beat()
00304         {
00305         iTicksLeft--;
00306         if(iTicksLeft<=0)
00307                 delete this;
00308         else
00309                 RandomDelay(iDelaySeed,iTimerNumber); // a random delay to mess up the timing
00310         }
00311 
00312 // private
00313 void CHeartbeatRunner::Synchronize()
00314         {
00315         TInt ticksMissed = 0;
00316                  // what time in microseconds should be for this tick
00317         TTime desiredTime = iStartTime + TTimeIntervalMicroSeconds((iTotalTicks - iTicksLeft) * iTickInterval);
00318         TTime currentTime; // set current time to now
00319         currentTime.HomeTime();
00320         TTimeIntervalMicroSeconds missedTime = currentTime.MicroSecondsFrom(desiredTime);
00321         // Calculate the ticks missed (quickly!)
00322         TInt64 missedTimeInt = missedTime.Int64(); // convert the missed time interval to an Int64
00323         
00324         ticksMissed = (missedTimeInt / iTickInterval);
00325         //ticksMissed = (missedTimeInt / iTickInterval).GetTInt();
00326         
00327         
00328         // The following loop increments the ticks missed by the same amount, but takes much longer
00329         // while (desiredTime < currentTime)
00330         //      {
00331         //      desiredTime = desiredTime - TTimeIntervalMicroSeconds(iTickInterval);
00332         //      ticksMissed++;
00333         //      }
00334         _LIT(KFormat4,"Ticks done %d\n");
00335         console->Printf(KFormat4, (iTotalTicks -iTicksLeft));
00336         
00337         iTicksLeft = iTicksLeft - ticksMissed;
00338         TTimeIntervalMicroSeconds elapsedTime;
00339         elapsedTime = currentTime.MicroSecondsFrom(iStartTime); // find and show elapsed time & ticks 
00340         _LIT(KFormat5,"Elapsed time: %Ld microseconds\n");
00341         console->Printf(KFormat5, elapsedTime.Int64());
00342         
00343         _LIT(KFormat6,"Synchronize heartbeat timer %d: ticks missed %d: left %d: done now %d\n");
00344         console->Printf(KFormat6,
00345                             iTimerNumber,
00346                                         ticksMissed,
00347                                         iTicksLeft,
00348                                         ((iTotalTicks - iTicksLeft) <= iTotalTicks) ? iTotalTicks - iTicksLeft : iTotalTicks
00349                                    );
00350         // iTicksLeft can be less than zero
00351         if(iTicksLeft<=0)
00352                 {
00353                 delete this;
00354                 }
00355         }
00356 
00357 /*
00358         TAppRunner class
00359 
00360         Encapsulates logic for stopping the active scheduler
00361 */
00362 
00363 
00364 // do the example
00365 
00366 void doExampleL()
00367     {
00368           // Make and install the active scheduler
00369         CActiveScheduler*  scheduler = new (ELeave) CActiveScheduler;
00370         
00371           // Push onto clean-up stack
00372         CleanupStack::PushL(scheduler); 
00373         
00374           // Install as active scheduler
00375         CActiveScheduler::Install(scheduler);
00376 
00377       // Controls the stopping of the scheduler
00378         TAppRunner appRunner; 
00379         
00380           // Set the tick interval to 1 second.
00381         TInt TickInterval = 1000000;
00382 
00383           // run each kind of timer for increasing numbers of ticks
00384           // was 10/40/10
00385                 
00386         for (TInt total_ticks=4; total_ticks<=6; total_ticks+=2)
00387                 {               
00388                   // Create periodic timer
00389                   //
00390                   // [nb Comment next line out if you just want to see heart beat runner]
00391         CPeriodicRunner::NewL(TickInterval, total_ticks, appRunner);
00392                         
00393                   // Create hearbeat
00394                   //
00395                   // [nb Comment next line out if you just want to see periodic timer]
00396                 CHeartbeatRunner::NewL(TickInterval, total_ticks, appRunner);
00397                 }
00398 
00399         CActiveScheduler::Start();
00400         CleanupStack::PopAndDestroy(); // scheduler
00401         }
00402 

Generated by  doxygen 1.6.2