Changeset 2206

Show
Ignore:
Timestamp:
27/07/2008 21:36:14 (5 months ago)
Author:
chris
Message:

New timer implementation using TimerQueue? on Windows to avoid the need
to create and manage a separate thread ourselves.

Location:
box/trunk/lib/common
Files:
2 modified

Legend:

Unmodified
Added
Removed
  • box/trunk/lib/common/Timer.cpp

    r2003 r2206  
    99// -------------------------------------------------------------------------- 
    1010 
     11#ifdef WIN32 
     12        #define _WIN32_WINNT 0x0500 
     13#endif 
     14 
    1115#include "Box.h" 
    1216 
     
    2024std::vector<Timer*>* Timers::spTimers = NULL; 
    2125bool Timers::sRescheduleNeeded = false; 
     26 
     27#define TIMER_ID "timer " << mName << " (" << this << ") " 
     28#define TIMER_ID_OF(t) "timer " << (t).GetName() << " (" << &(t) << ") " 
    2229 
    2330typedef void (*sighandler_t)(int); 
     
    3643         
    3744        #if defined WIN32 && ! defined PLATFORM_CYGWIN 
    38                 // no support for signals at all 
    39                 InitTimer(); 
    40                 SetTimerHandler(Timers::SignalHandler); 
     45                // no init needed 
    4146        #else 
    4247                struct sigaction newact, oldact; 
     
    7378         
    7479        #if defined WIN32 && ! defined PLATFORM_CYGWIN 
    75                 // no support for signals at all 
    76                 FiniTimer(); 
    77                 SetTimerHandler(NULL); 
     80                // no cleanup needed 
    7881        #else 
    7982                struct itimerval timeout; 
     
    150153} 
    151154 
     155void Timers::RequestReschedule() 
     156{ 
     157        sRescheduleNeeded = true; 
     158} 
     159 
     160void Timers::RescheduleIfNeeded() 
     161{ 
     162        if (sRescheduleNeeded)  
     163        { 
     164                Reschedule(); 
     165        } 
     166} 
     167 
    152168#define FORMAT_MICROSECONDS(t) \ 
    153169        (int)(t / 1000000) << "." << \ 
    154         (int)(t % 1000000) 
     170        (int)(t % 1000000) << " seconds" 
    155171 
    156172// -------------------------------------------------------------------------- 
     
    196212        sRescheduleNeeded = false; 
    197213 
     214#ifdef WIN32 
     215        // win32 timers need no management 
     216#else 
    198217        box_time_t timeNow = GetCurrentBoxTime(); 
    199218 
     
    213232                        if (timeToExpiry <= 0) 
    214233                        { 
     234                                /* 
    215235                                BOX_TRACE("timer " << *i << " has expired, " 
    216236                                        "triggering it"); 
     237                                */ 
     238                                BOX_TRACE(TIMER_ID_OF(**i) "has expired, " 
     239                                        "triggering " << 
     240                                        FORMAT_MICROSECONDS(-timeToExpiry) << 
     241                                        " late"); 
    217242                                rTimer.OnExpire(); 
    218243                                spTimers->erase(i); 
     
    222247                        else 
    223248                        { 
     249                                /* 
    224250                                BOX_TRACE("timer " << *i << " has not " 
    225251                                        "expired, triggering in " << 
    226252                                        FORMAT_MICROSECONDS(timeToExpiry) << 
    227253                                        " seconds"); 
     254                                */ 
    228255                        } 
    229256                } 
     
    234261                         
    235262        int64_t timeToNextEvent = 0; 
     263        std::string nameOfNextEvent; 
    236264 
    237265        for (std::vector<Timer*>::iterator i = spTimers->begin(); 
     
    241269                int64_t timeToExpiry = rTimer.GetExpiryTime() - timeNow; 
    242270 
     271                ASSERT(timeToExpiry > 0) 
    243272                if (timeToExpiry <= 0) 
    244273                { 
     
    249278                { 
    250279                        timeToNextEvent = timeToExpiry; 
     280                        nameOfNextEvent = rTimer.GetName(); 
    251281                } 
    252282        } 
    253283         
    254284        ASSERT(timeToNextEvent >= 0); 
    255          
     285 
     286        if (timeToNextEvent == 0) 
     287        { 
     288                BOX_TRACE("timer: no more events, going to sleep."); 
     289        } 
     290        else 
     291        { 
     292                BOX_TRACE("timer: next event: " << nameOfNextEvent << 
     293                        " expires in " << FORMAT_MICROSECONDS(timeToNextEvent)); 
     294        } 
     295 
    256296        struct itimerval timeout; 
    257297        memset(&timeout, 0, sizeof(timeout)); 
     
    259299        timeout.it_value.tv_sec  = BoxTimeToSeconds(timeToNextEvent); 
    260300        timeout.it_value.tv_usec = (int) 
    261                 (BoxTimeToMicroSeconds(timeToNextEvent) % MICRO_SEC_IN_SEC); 
     301                (BoxTimeToMicroSeconds(timeToNextEvent) 
     302                % MICRO_SEC_IN_SEC); 
    262303 
    263304        if(::setitimer(ITIMER_REAL, &timeout, NULL) != 0) 
    264305        { 
    265                 BOX_ERROR("Failed to initialise timer\n"); 
     306                BOX_ERROR("Failed to initialise system timer\n"); 
    266307                THROW_EXCEPTION(CommonException, Internal) 
    267308        } 
     309#endif 
    268310} 
    269311 
     
    280322// 
    281323// -------------------------------------------------------------------------- 
    282 void Timers::SignalHandler(int iUnused) 
     324void Timers::SignalHandler(int unused) 
    283325{ 
    284326        // ASSERT(spTimers); 
     
    286328} 
    287329 
    288 Timer::Timer(size_t timeoutSecs) 
     330// -------------------------------------------------------------------------- 
     331// 
     332// Function 
     333//              Name:    Timer::Timer(size_t timeoutSecs, 
     334//                       const std::string& rName) 
     335//              Purpose: Standard timer constructor, takes a timeout in 
     336//                       seconds from now, and an optional name for 
     337//                       logging purposes. 
     338//              Created: 27/07/2008 
     339// 
     340// -------------------------------------------------------------------------- 
     341 
     342Timer::Timer(size_t timeoutSecs, const std::string& rName) 
    289343: mExpires(GetCurrentBoxTime() + SecondsToBoxTime(timeoutSecs)), 
    290   mExpired(false) 
     344  mExpired(false), 
     345  mName(rName) 
     346#ifdef WIN32 
     347, mTimerHandle(INVALID_HANDLE_VALUE) 
     348#endif 
    291349{ 
    292350        #ifndef NDEBUG 
    293351        if (timeoutSecs == 0) 
    294352        { 
    295                 BOX_TRACE("timer " << this << " initialised for " << 
    296                         timeoutSecs << " secs, will not fire"); 
     353                BOX_TRACE(TIMER_ID "initialised for " << timeoutSecs <<  
     354                        " secs, will not fire"); 
    297355        } 
    298356        else 
    299357        { 
    300                 BOX_TRACE("timer " << this << " initialised for " << 
    301                         timeoutSecs << " secs, to fire at " << 
    302                         FORMAT_MICROSECONDS(mExpires)); 
     358                BOX_TRACE(TIMER_ID "initialised for " << timeoutSecs << 
     359                        " secs, to fire at " << FormatTime(mExpires, true)); 
    303360        } 
    304361        #endif 
     
    311368        { 
    312369                Timers::Add(*this); 
    313         } 
    314 } 
     370                Start(timeoutSecs * MICRO_SEC_IN_SEC_LL); 
     371        } 
     372} 
     373 
     374// -------------------------------------------------------------------------- 
     375// 
     376// Function 
     377//              Name:    Timer::Start() 
     378//              Purpose: This internal function initialises an OS TimerQueue 
     379//                       timer on Windows, while on Unixes there is only a 
     380//                       single global timer, managed by the Timers class, 
     381//                       so this method does nothing. 
     382//              Created: 27/07/2008 
     383// 
     384// -------------------------------------------------------------------------- 
     385 
     386void Timer::Start() 
     387{ 
     388#ifdef WIN32 
     389        box_time_t timeNow = GetCurrentBoxTime(); 
     390        int64_t timeToExpiry = mExpires - timeNow; 
     391 
     392        if (timeToExpiry <= 0) 
     393        { 
     394                BOX_WARNING(TIMER_ID << "fudging expiry from -" << 
     395                        FORMAT_MICROSECONDS(-timeToExpiry)) 
     396                timeToExpiry = 1; 
     397        } 
     398 
     399        Start(timeToExpiry); 
     400#endif 
     401} 
     402 
     403// -------------------------------------------------------------------------- 
     404// 
     405// Function 
     406//              Name:    Timer::Start(int64_t delayInMicros) 
     407//              Purpose: This internal function initialises an OS TimerQueue 
     408//                       timer on Windows, with a specified delay already 
     409//                       calculated to save us doing it again. Like 
     410//                       Timer::Start(), on Unixes it does nothing. 
     411//              Created: 27/07/2008 
     412// 
     413// -------------------------------------------------------------------------- 
     414 
     415void Timer::Start(int64_t delayInMicros) 
     416{ 
     417#ifdef WIN32 
     418        // only call me once! 
     419        ASSERT(mTimerHandle == INVALID_HANDLE_VALUE); 
     420 
     421        int64_t delayInMillis = delayInMicros / 1000; 
     422 
     423        // Windows XP always seems to fire timers up to 20 ms late, 
     424        // at least on my test laptop. Not critical in practice, but our 
     425        // tests are precise enough that they will fail if we don't 
     426        // correct for it. 
     427        delayInMillis -= 20; 
     428         
     429        // Set a system timer to call our timer routine 
     430        if (CreateTimerQueueTimer(&mTimerHandle, NULL, TimerRoutine, 
     431                (PVOID)this, delayInMillis, 0, WT_EXECUTEINTIMERTHREAD) 
     432                == FALSE) 
     433        { 
     434                BOX_ERROR(TIMER_ID "failed to create timer: " << 
     435                        GetErrorMessage(GetLastError())); 
     436                mTimerHandle = INVALID_HANDLE_VALUE; 
     437        } 
     438#endif 
     439} 
     440 
     441// -------------------------------------------------------------------------- 
     442// 
     443// Function 
     444//              Name:    Timer::Stop() 
     445//              Purpose: This internal function deletes the associated OS 
     446//                       TimerQueue timer on Windows, and on Unixes does 
     447//                       nothing. 
     448//              Created: 27/07/2008 
     449// 
     450// -------------------------------------------------------------------------- 
     451 
     452void Timer::Stop() 
     453{ 
     454#ifdef WIN32 
     455        if (mTimerHandle != INVALID_HANDLE_VALUE) 
     456        { 
     457                if (DeleteTimerQueueTimer(NULL, mTimerHandle, 
     458                        INVALID_HANDLE_VALUE) == FALSE) 
     459                { 
     460                        BOX_ERROR(TIMER_ID "failed to delete timer: " << 
     461                                GetErrorMessage(GetLastError())); 
     462                } 
     463                mTimerHandle = INVALID_HANDLE_VALUE; 
     464        } 
     465#endif 
     466} 
     467 
     468// -------------------------------------------------------------------------- 
     469// 
     470// Function 
     471//              Name:    Timer::~Timer() 
     472//              Purpose: Destructor for Timer objects. 
     473//              Created: 27/07/2008 
     474// 
     475// -------------------------------------------------------------------------- 
    315476 
    316477Timer::~Timer() 
    317478{ 
    318479        #ifndef NDEBUG 
    319         BOX_TRACE("timer " << this << " destroyed"); 
     480        BOX_TRACE(TIMER_ID "destroyed"); 
    320481        #endif 
    321482 
    322483        Timers::Remove(*this); 
    323 } 
     484        Stop(); 
     485} 
     486 
     487// -------------------------------------------------------------------------- 
     488// 
     489// Function 
     490//              Name:    Timer::Timer(Timer& rToCopy) 
     491//              Purpose: Copy constructor for Timer objects. Creates a new 
     492//                       timer that will trigger at the same time as the 
     493//                       original. The original will usually be discarded. 
     494//              Created: 27/07/2008 
     495// 
     496// -------------------------------------------------------------------------- 
    324497 
    325498Timer::Timer(const Timer& rToCopy) 
    326499: mExpires(rToCopy.mExpires), 
    327   mExpired(rToCopy.mExpired) 
     500  mExpired(rToCopy.mExpired), 
     501  mName(rToCopy.mName) 
     502#ifdef WIN32 
     503, mTimerHandle(INVALID_HANDLE_VALUE) 
     504#endif 
    328505{ 
    329506        #ifndef NDEBUG 
    330507        if (mExpired) 
    331508        { 
    332                 BOX_TRACE("timer " << this << " initialised from timer " << 
    333                         &rToCopy << ", already expired, will not fire"); 
     509                BOX_TRACE(TIMER_ID "initialised from timer " << &rToCopy << ", " 
     510                        "already expired, will not fire"); 
    334511        } 
    335512        else if (mExpires == 0) 
    336513        { 
    337                 BOX_TRACE("timer " << this << " initialised from timer " << 
    338                         &rToCopy << ", no expiry, will not fire"); 
     514                BOX_TRACE(TIMER_ID "initialised from timer " << &rToCopy << ", " 
     515                        "no expiry, will not fire"); 
    339516        } 
    340517        else 
    341518        { 
    342                 BOX_TRACE("timer " << this << " initialised from timer " << 
    343                         &rToCopy << " to fire at " << 
     519                BOX_TRACE(TIMER_ID "initialised from timer " << &rToCopy << ", " 
     520                        "to fire at " << 
    344521                        (int)(mExpires / 1000000) << "." << 
    345522                        (int)(mExpires % 1000000)); 
     
    350527        { 
    351528                Timers::Add(*this); 
    352         } 
    353 } 
     529                Start(); 
     530        } 
     531} 
     532 
     533// -------------------------------------------------------------------------- 
     534// 
     535// Function 
     536//              Name:    Timer::operator=(const Timer& rToCopy) 
     537//              Purpose: Assignment operator for Timer objects. Works 
     538//                       exactly the same as the copy constructor, except 
     539//                       that if the receiving timer is already running, 
     540//                       it is stopped first. 
     541//              Created: 27/07/2008 
     542// 
     543// -------------------------------------------------------------------------- 
    354544 
    355545Timer& Timer::operator=(const Timer& rToCopy) 
     
    358548        if (rToCopy.mExpired) 
    359549        { 
    360                 BOX_TRACE("timer " << this << " initialised from timer " << 
    361                         &rToCopy << ", already expired, will not fire"); 
     550                BOX_TRACE(TIMER_ID "initialised from timer " << &rToCopy << ", " 
     551                        "already expired, will not fire"); 
    362552        } 
    363553        else if (rToCopy.mExpires == 0) 
    364554        { 
    365                 BOX_TRACE("timer " << this << " initialised from timer " << 
    366                         &rToCopy << ", no expiry, will not fire"); 
     555                BOX_TRACE(TIMER_ID "initialised from timer " << &rToCopy << ", " 
     556                        "no expiry, will not fire"); 
    367557        } 
    368558        else 
    369559        { 
    370                 BOX_TRACE("timer " << this << " initialised from timer " << 
    371                         &rToCopy << " to fire at " << 
     560                BOX_TRACE(TIMER_ID "initialised from timer " << &rToCopy << ", " 
     561                        "to fire at " << 
    372562                        (int)(rToCopy.mExpires / 1000000) << "." << 
    373563                        (int)(rToCopy.mExpires % 1000000)); 
     
    376566 
    377567        Timers::Remove(*this); 
     568        Stop(); 
     569 
    378570        mExpires = rToCopy.mExpires; 
    379571        mExpired = rToCopy.mExpired; 
     572 
    380573        if (!mExpired && mExpires != 0) 
    381574        { 
    382575                Timers::Add(*this); 
    383         } 
     576                Start(); 
     577        } 
     578 
    384579        return *this; 
    385580} 
    386581 
     582// -------------------------------------------------------------------------- 
     583// 
     584// Function 
     585//              Name:    Timer::OnExpire() 
     586//              Purpose: Method called by Timers::Reschedule (on Unixes) 
     587//                       on next poll after timer expires, or from 
     588//                       Timer::TimerRoutine (on Windows) from a separate 
     589//                       thread managed by the OS. Marks the timer as 
     590//                       expired for future reference. 
     591//              Created: 27/07/2008 
     592// 
     593// -------------------------------------------------------------------------- 
     594 
    387595void Timer::OnExpire() 
    388596{ 
    389597        #ifndef NDEBUG 
    390         BOX_TRACE("timer " << this << " fired"); 
     598        BOX_TRACE(TIMER_ID "fired"); 
    391599        #endif 
    392600 
    393601        mExpired = true; 
    394602} 
     603 
     604// -------------------------------------------------------------------------- 
     605// 
     606// Function 
     607//              Name:    Timer::TimerRoutine(PVOID lpParam, 
     608//                       BOOLEAN TimerOrWaitFired) 
     609//              Purpose: Static method called by the Windows OS when a 
     610//                       TimerQueue timer expires. 
     611//              Created: 27/07/2008 
     612// 
     613// -------------------------------------------------------------------------- 
     614 
     615#ifdef WIN32 
     616VOID CALLBACK Timer::TimerRoutine(PVOID lpParam, 
     617        BOOLEAN TimerOrWaitFired) 
     618{ 
     619        Timer* pTimer = (Timer*)lpParam; 
     620        pTimer->OnExpire(); 
     621        // is it safe to write to write debug output from a timer? 
     622        // e.g. to write to the Event Log? 
     623} 
     624#endif 
  • box/trunk/lib/common/Timer.h

    r1758 r2206  
    4141        static bool sRescheduleNeeded; 
    4242        static void SignalHandler(int iUnused); 
    43          
     43 
    4444        public: 
    4545        static void Init(); 
     
    4747        static void Add   (Timer& rTimer); 
    4848        static void Remove(Timer& rTimer); 
    49         static void RequestReschedule() 
    50         { 
    51                 sRescheduleNeeded = true; 
    52         } 
    53  
    54         static void RescheduleIfNeeded() 
    55         { 
    56                 if (sRescheduleNeeded)  
    57                 { 
    58                         Reschedule(); 
    59                 } 
    60         } 
     49        static void RequestReschedule(); 
     50        static void RescheduleIfNeeded(); 
    6151}; 
    6252 
     
    6454{ 
    6555public: 
    66         Timer(size_t timeoutSecs); 
     56        Timer(size_t timeoutSecs, const std::string& rName = ""); 
    6757        virtual ~Timer(); 
    6858        Timer(const Timer &); 
    6959        Timer &operator=(const Timer &); 
    7060 
    71 public: 
    7261        box_time_t   GetExpiryTime() { return mExpires; } 
    7362        virtual void OnExpire(); 
     
    7766                return mExpired;  
    7867        } 
     68 
     69        const std::string& GetName() const { return mName; } 
    7970         
    8071private: 
    81         box_time_t mExpires; 
    82         bool       mExpired; 
     72        box_time_t  mExpires; 
     73        bool        mExpired; 
     74        std::string mName; 
     75 
     76        void Start(); 
     77        void Start(int64_t delayInMicros); 
     78        void Stop(); 
     79 
     80        #ifdef WIN32 
     81        HANDLE mTimerHandle; 
     82        static VOID CALLBACK TimerRoutine(PVOID lpParam, 
     83                BOOLEAN TimerOrWaitFired); 
     84        #endif 
    8385}; 
    8486