/////////////////////////////////////////////////////////////////////////////// // // Copyright (c) 2000, Microsoft Corp. All rights reserved. // // FILE // // timerq.cpp // // SYNOPSIS // // Defines the class TimerQueue. // // MODIFICATION HISTORY // // 02/10/2000 Original version. // /////////////////////////////////////////////////////////////////////////////// #include #include void Timer::cancelTimer() throw () { TimerQueue* ourQueue = queue; if (ourQueue) { ourQueue->cancelTimer(this); } } TimerQueue::TimerQueue() : idle(TRUE), hasWatcher(false) { CallbackRoutine = startRoutine; // We bump up the use count to prevent the idle event from being // continuously set and reset. ++useCount; } TimerQueue::~TimerQueue() throw () { cancelAllTimers(); } inline void TimerQueue::createWatcher() throw () { if (IASRequestThread(this)) { hasWatcher = TRUE; } } bool TimerQueue::setTimer( Timer* timer, ULONG dueTime, ULONG period ) throw () { // Make sure it's not already set. timer->cancelTimer(); // Set the timer's period. timer->period = period; monitor.lock(); // Add it to the queue ... bool success = queueTimer(timer, dueTime); // .. and ensure we have someone to watch it. if (success && !hasWatcher) { createWatcher(); } monitor.unlock(); return success; } void TimerQueue::cancelAllTimers() throw () { monitor.lock(); // Release the references. for (TimerIterator i = queue.begin(); i != queue.end(); ++i) { i->second->queue = NULL; i->second->Release(); } // Clear the queue. queue.clear(); // Allow the use count to go to zero, so the idle event will be set. if (--useCount == 0) { idle.set(); } monitor.unlock(); // Wake up the watcher, so he'll see the queue is empty. nudge.set(); // Wait until we're idle, ... idle.wait(); // ... then put the useCount back up. ++useCount; } void TimerQueue::cancelTimer(Timer* timer) throw () { monitor.lock(); // Make sure this is from the right queue. We checked this is // Timer::cancelTimer, but we need to do it again now that we hold the lock. if (timer->queue == this) { // Remove it from the queue. queue.erase(timer->self); timer->queue = NULL; // Release the reference we added in queueTimer. timer->Release(); // Normally, we won't bother to wake the watcher, but if this was the last // timer, we'll let him exit. if (queue.empty()) { nudge.set(); } } monitor.unlock(); } void TimerQueue::executeOneTimer() throw () { // The timer to be executed. Timer* timer = NULL; monitor.lock(); // Loop until either we get a timer or the queue is empty. while (!queue.empty()) { ULONG64 now = GetSystemTime64(); // Has the next timer expired ? if (now >= queue.begin()->first) { // Yes, so save it for later. timer = queue.begin()->second; // Remove it from the queue. queue.erase(queue.begin()); timer->queue = NULL; // If it's recurrent, set it again. if (timer->period) { queueTimer(timer, timer->period); } break; } // Calculate the time until the next timer expires ... ULONG timeout = (queue.begin()->first - now) / 10000; monitor.unlock(); // ... and wait. nudge.wait(timeout); monitor.lock(); } // We're not watching the queue anymore. hasWatcher = false; // Do we need a replacement? if (!queue.empty()) { createWatcher(); } monitor.unlock(); if (timer) { // Invoke the callback. timer->onExpiry(); // Release the reference we added in queueTimer. timer->Release(); } // We're exiting, so drop the useCount. if (--useCount == 0) { idle.set(); } } bool TimerQueue::queueTimer(Timer* timer, ULONG dueTime) throw () { // NOTE: You must hold the lock when calling this method. ULONG64 expiry = GetSystemTime64() + dueTime * 10000i64; bool success; try { // Insert the timer into the queue. timer->self = queue.insert(Timers::value_type(expiry, timer)); // If this is the next timer to expire, we need to nudge the watcher. if (timer->self == queue.begin()) { nudge.set(); } timer->queue = this; timer->AddRef(); success = true; } catch (...) { success = false; } return success; } void TimerQueue::startRoutine(PIAS_CALLBACK This) throw () { static_cast(This)->executeOneTimer(); }