windows-nt/Source/XPSP1/NT/net/ias/services/thrdpool/timer.h
2020-09-26 16:20:57 +08:00

323 lines
6.6 KiB
C++

///////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 1997, Microsoft Corp. All rights reserved.
//
// FILE
//
// Timer.h
//
// SYNOPSIS
//
// This file describes the class TimerQueue.
//
// MODIFICATION HISTORY
//
// 11/26/1997 Original version.
// 4/16/1997 Completely clean-up in Finalize().
//
///////////////////////////////////////////////////////////////////////////////
#ifndef _TIMER_H_
#define _TIMER_H_
#include <guard.h>
#include <hashtbl.h>
#include <nocopy.h>
#include <set>
using std::set;
///////////////////////////////////////////////////////////////////////////////
//
// CLASS
//
// TimerQueue
//
// DESCRIPTION
//
// This class maintains a queue of timers.
//
///////////////////////////////////////////////////////////////////////////////
class TimerQueue
: Guardable, NonCopyable
{
public:
void Initialize();
void Finalize();
HANDLE Set(DWORD dwMilliseconds, PIAS_CALLBACK pOnExpiry);
PIAS_CALLBACK Cancel(HANDLE handle);
protected:
// Utility function to get the system time as a 64-bit integer.
static DWORDLONG GetCurrentTime()
{
FILETIME ft;
GetSystemTimeAsFileTime(&ft);
return (DWORDLONG)ft.dwHighDateTime << 32 | ft.dwLowDateTime;
}
struct Timer;
friend struct Timer;
// Function class to compare timer pointers.
struct ByExpiry {
bool operator()(const Timer* t1, const Timer* t2) const;
};
typedef set<Timer*, ByExpiry> TIMER_LIST;
typedef TIMER_LIST::iterator ITERATOR;
//////////
// Struct used to represent an individual timer.
//////////
struct Timer : NonCopyable
{
Timer(DWORD dwMilliseconds, PIAS_CALLBACK pOnExpiry)
: expiry(GetCurrentTime() + 10000i64 * dwMilliseconds),
onExpiry(pOnExpiry)
{ }
bool operator<(const Timer& timer) const
{
return (expiry < timer.expiry) ||
(expiry == timer.expiry && handle < timer.handle);
}
IAS_USE_SMALL_BLOCK_POOL();
DWORDLONG expiry; // The expiration time.
DWORD period; // The period of the timer in msec.
DWORD handle; // Unique ID for the timer.
PIAS_CALLBACK onExpiry; // Callback triggered when timer expires.
ITERATOR iterator; // Location of the timer in the timer list.
};
// Function class to extract the handle.
struct ExtractHandle {
DWORD operator()(const Timer* t1) const { return t1->handle; }
};
// Function class to hash a handle.
struct HashHandle {
DWORD operator()(DWORD handle) const { return handle; }
};
typedef hash_table<DWORD, HashHandle, Timer*, ExtractHandle> HANDLE_MAP;
DWORD TimeToNextExpiry() const;
Timer* GetExpiredTimer();
void WatchTimers();
DWORD nextHandle; // Next handle to assign.
TIMER_LIST timers; // timers sorted by expiry.
HANDLE_MAP handleMap; // timers mapped by handle.
bool done; // Flag to shutdown the watcher thread.
HANDLE nudge; // Event to knock watcher out of wait.
HANDLE exited; // Event signalling watcher has exited.
};
inline bool TimerQueue::ByExpiry::operator()(
const TimerQueue::Timer* t1,
const TimerQueue::Timer* t2
) const
{
return *t1 < *t2;
}
void TimerQueue::Initialize()
{
nextHandle = 0;
done = false;
nudge = CreateEvent(NULL, FALSE, FALSE, NULL);
exited = CreateEvent(NULL, TRUE, FALSE, NULL);
IASRequestThread(MakeBoundCallback(this, &TimerQueue::WatchTimers));
}
void TimerQueue::Finalize()
{
done = true;
SetEvent(nudge);
WaitForSingleObject(exited, INFINITE);
CloseHandle(exited);
CloseHandle(nudge);
handleMap.clear();
timers.clear();
}
HANDLE TimerQueue::Set(DWORD dwMilliseconds, PIAS_CALLBACK pOnExpiry)
{
if (!pOnExpiry)
{
SetLastError(ERROR_INVALID_PARAMETER);
return NULL;
}
Timer* timer = new Timer(dwMilliseconds, pOnExpiry);
if (timer == NULL)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return NULL;
}
Lock();
timer->handle = ++nextHandle;
// Make sure we never give out zero.
if (timer->handle == 0) timer->handle = ++nextHandle;
// Insert the timer into the queue ...
timer->iterator = timers.insert(timer).first;
// ... and the handle map.
handleMap.insert(timer);
// If this is the next timer to expire, then we need to reset the watcher.
bool IsNext = (timer == *timers.begin());
Unlock();
if (IsNext) SetEvent(nudge);
// Timers are opaque to the outside world.
return (HANDLE)timer->handle;
}
PIAS_CALLBACK TimerQueue::Cancel(HANDLE handle)
{
_serialize
Timer* const* node = handleMap.find((DWORD)handle);
if (!node)
{
SetLastError(ERROR_NOT_FOUND);
return NULL;
}
Timer* timer = *node;
if (timer->iterator == timers.end())
{
// If the timer is in the handle map, but not the queue, then it must
// be currently executing.
SetLastError(ERROR_IO_PENDING);
return NULL;
}
timers.erase(timer->iterator);
handleMap.erase((DWORD)handle);
PIAS_CALLBACK Callback = timer->onExpiry;
delete timer;
return Callback;
}
DWORD TimerQueue::TimeToNextExpiry() const
{
//////////
// NOT SERIALIZED
//////////
if (timers.empty()) return INFINITE;
DWORDLONG Now = GetCurrentTime();
DWORDLONG Next = (*timers.begin())->expiry;
// Has the next timer expired?
return (Next > Now) ? (Next - Now)/10000 : 0;
}
TimerQueue::Timer* TimerQueue::GetExpiredTimer()
{
// We loop until either the done flag is set or a timer expires.
while (true)
{
if (done) return NULL;
Lock();
DWORD Timeout = TimeToNextExpiry();
if (Timeout == 0) break;
Unlock();
// The next timer hasn't expired yet, so wait.
WaitForSingleObject(nudge, Timeout);
}
//////////
// If we made it here, a timer has expired.
//////////
Timer* timer = *timers.begin();
// Remove it from the queue ...
timers.erase(timer->iterator);
// ... and reset the iterator.
timer->iterator = timers.end();
Unlock();
return timer;
}
void TimerQueue::WatchTimers()
{
Timer* timer = GetExpiredTimer();
if (timer == NULL)
{
SetEvent(exited);
return;
}
// Get a new watcher thread to take our place.
IASRequestThread(MakeBoundCallback(this, &TimerQueue::WatchTimers));
timer->onExpiry->CallbackRoutine(timer->onExpiry);
Lock();
handleMap.erase(timer->handle);
Unlock();
delete timer;
}
#endif // _TIMER_H_