windows-nt/Source/XPSP1/NT/net/ias/protocol/proxy/timerq.cpp

222 lines
4.6 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
///////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2000, Microsoft Corp. All rights reserved.
//
// FILE
//
// timerq.cpp
//
// SYNOPSIS
//
// Defines the class TimerQueue.
//
// MODIFICATION HISTORY
//
// 02/10/2000 Original version.
//
///////////////////////////////////////////////////////////////////////////////
#include <proxypch.h>
#include <timerq.h>
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<TimerQueue*>(This)->executeOneTimer();
}