examples/Base/Timers/Periodic/Periodic.cpp

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

Generated by  doxygen 1.6.2