Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

322 lines
6.6 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (c) 1997, Microsoft Corp. All rights reserved.
  4. //
  5. // FILE
  6. //
  7. // Timer.h
  8. //
  9. // SYNOPSIS
  10. //
  11. // This file describes the class TimerQueue.
  12. //
  13. // MODIFICATION HISTORY
  14. //
  15. // 11/26/1997 Original version.
  16. // 4/16/1997 Completely clean-up in Finalize().
  17. //
  18. ///////////////////////////////////////////////////////////////////////////////
  19. #ifndef _TIMER_H_
  20. #define _TIMER_H_
  21. #include <guard.h>
  22. #include <hashtbl.h>
  23. #include <nocopy.h>
  24. #include <set>
  25. using std::set;
  26. ///////////////////////////////////////////////////////////////////////////////
  27. //
  28. // CLASS
  29. //
  30. // TimerQueue
  31. //
  32. // DESCRIPTION
  33. //
  34. // This class maintains a queue of timers.
  35. //
  36. ///////////////////////////////////////////////////////////////////////////////
  37. class TimerQueue
  38. : Guardable, NonCopyable
  39. {
  40. public:
  41. void Initialize();
  42. void Finalize();
  43. HANDLE Set(DWORD dwMilliseconds, PIAS_CALLBACK pOnExpiry);
  44. PIAS_CALLBACK Cancel(HANDLE handle);
  45. protected:
  46. // Utility function to get the system time as a 64-bit integer.
  47. static DWORDLONG GetCurrentTime()
  48. {
  49. FILETIME ft;
  50. GetSystemTimeAsFileTime(&ft);
  51. return (DWORDLONG)ft.dwHighDateTime << 32 | ft.dwLowDateTime;
  52. }
  53. struct Timer;
  54. friend struct Timer;
  55. // Function class to compare timer pointers.
  56. struct ByExpiry {
  57. bool operator()(const Timer* t1, const Timer* t2) const;
  58. };
  59. typedef set<Timer*, ByExpiry> TIMER_LIST;
  60. typedef TIMER_LIST::iterator ITERATOR;
  61. //////////
  62. // Struct used to represent an individual timer.
  63. //////////
  64. struct Timer : NonCopyable
  65. {
  66. Timer(DWORD dwMilliseconds, PIAS_CALLBACK pOnExpiry)
  67. : expiry(GetCurrentTime() + 10000i64 * dwMilliseconds),
  68. onExpiry(pOnExpiry)
  69. { }
  70. bool operator<(const Timer& timer) const
  71. {
  72. return (expiry < timer.expiry) ||
  73. (expiry == timer.expiry && handle < timer.handle);
  74. }
  75. IAS_USE_SMALL_BLOCK_POOL();
  76. DWORDLONG expiry; // The expiration time.
  77. DWORD period; // The period of the timer in msec.
  78. DWORD handle; // Unique ID for the timer.
  79. PIAS_CALLBACK onExpiry; // Callback triggered when timer expires.
  80. ITERATOR iterator; // Location of the timer in the timer list.
  81. };
  82. // Function class to extract the handle.
  83. struct ExtractHandle {
  84. DWORD operator()(const Timer* t1) const { return t1->handle; }
  85. };
  86. // Function class to hash a handle.
  87. struct HashHandle {
  88. DWORD operator()(DWORD handle) const { return handle; }
  89. };
  90. typedef hash_table<DWORD, HashHandle, Timer*, ExtractHandle> HANDLE_MAP;
  91. DWORD TimeToNextExpiry() const;
  92. Timer* GetExpiredTimer();
  93. void WatchTimers();
  94. DWORD nextHandle; // Next handle to assign.
  95. TIMER_LIST timers; // timers sorted by expiry.
  96. HANDLE_MAP handleMap; // timers mapped by handle.
  97. bool done; // Flag to shutdown the watcher thread.
  98. HANDLE nudge; // Event to knock watcher out of wait.
  99. HANDLE exited; // Event signalling watcher has exited.
  100. };
  101. inline bool TimerQueue::ByExpiry::operator()(
  102. const TimerQueue::Timer* t1,
  103. const TimerQueue::Timer* t2
  104. ) const
  105. {
  106. return *t1 < *t2;
  107. }
  108. void TimerQueue::Initialize()
  109. {
  110. nextHandle = 0;
  111. done = false;
  112. nudge = CreateEvent(NULL, FALSE, FALSE, NULL);
  113. exited = CreateEvent(NULL, TRUE, FALSE, NULL);
  114. IASRequestThread(MakeBoundCallback(this, &TimerQueue::WatchTimers));
  115. }
  116. void TimerQueue::Finalize()
  117. {
  118. done = true;
  119. SetEvent(nudge);
  120. WaitForSingleObject(exited, INFINITE);
  121. CloseHandle(exited);
  122. CloseHandle(nudge);
  123. handleMap.clear();
  124. timers.clear();
  125. }
  126. HANDLE TimerQueue::Set(DWORD dwMilliseconds, PIAS_CALLBACK pOnExpiry)
  127. {
  128. if (!pOnExpiry)
  129. {
  130. SetLastError(ERROR_INVALID_PARAMETER);
  131. return NULL;
  132. }
  133. Timer* timer = new Timer(dwMilliseconds, pOnExpiry);
  134. if (timer == NULL)
  135. {
  136. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  137. return NULL;
  138. }
  139. Lock();
  140. timer->handle = ++nextHandle;
  141. // Make sure we never give out zero.
  142. if (timer->handle == 0) timer->handle = ++nextHandle;
  143. // Insert the timer into the queue ...
  144. timer->iterator = timers.insert(timer).first;
  145. // ... and the handle map.
  146. handleMap.insert(timer);
  147. // If this is the next timer to expire, then we need to reset the watcher.
  148. bool IsNext = (timer == *timers.begin());
  149. Unlock();
  150. if (IsNext) SetEvent(nudge);
  151. // Timers are opaque to the outside world.
  152. return (HANDLE)timer->handle;
  153. }
  154. PIAS_CALLBACK TimerQueue::Cancel(HANDLE handle)
  155. {
  156. _serialize
  157. Timer* const* node = handleMap.find((DWORD)handle);
  158. if (!node)
  159. {
  160. SetLastError(ERROR_NOT_FOUND);
  161. return NULL;
  162. }
  163. Timer* timer = *node;
  164. if (timer->iterator == timers.end())
  165. {
  166. // If the timer is in the handle map, but not the queue, then it must
  167. // be currently executing.
  168. SetLastError(ERROR_IO_PENDING);
  169. return NULL;
  170. }
  171. timers.erase(timer->iterator);
  172. handleMap.erase((DWORD)handle);
  173. PIAS_CALLBACK Callback = timer->onExpiry;
  174. delete timer;
  175. return Callback;
  176. }
  177. DWORD TimerQueue::TimeToNextExpiry() const
  178. {
  179. //////////
  180. // NOT SERIALIZED
  181. //////////
  182. if (timers.empty()) return INFINITE;
  183. DWORDLONG Now = GetCurrentTime();
  184. DWORDLONG Next = (*timers.begin())->expiry;
  185. // Has the next timer expired?
  186. return (Next > Now) ? (Next - Now)/10000 : 0;
  187. }
  188. TimerQueue::Timer* TimerQueue::GetExpiredTimer()
  189. {
  190. // We loop until either the done flag is set or a timer expires.
  191. while (true)
  192. {
  193. if (done) return NULL;
  194. Lock();
  195. DWORD Timeout = TimeToNextExpiry();
  196. if (Timeout == 0) break;
  197. Unlock();
  198. // The next timer hasn't expired yet, so wait.
  199. WaitForSingleObject(nudge, Timeout);
  200. }
  201. //////////
  202. // If we made it here, a timer has expired.
  203. //////////
  204. Timer* timer = *timers.begin();
  205. // Remove it from the queue ...
  206. timers.erase(timer->iterator);
  207. // ... and reset the iterator.
  208. timer->iterator = timers.end();
  209. Unlock();
  210. return timer;
  211. }
  212. void TimerQueue::WatchTimers()
  213. {
  214. Timer* timer = GetExpiredTimer();
  215. if (timer == NULL)
  216. {
  217. SetEvent(exited);
  218. return;
  219. }
  220. // Get a new watcher thread to take our place.
  221. IASRequestThread(MakeBoundCallback(this, &TimerQueue::WatchTimers));
  222. timer->onExpiry->CallbackRoutine(timer->onExpiry);
  223. Lock();
  224. handleMap.erase(timer->handle);
  225. Unlock();
  226. delete timer;
  227. }
  228. #endif // _TIMER_H_