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.

221 lines
4.6 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (c) 2000, Microsoft Corp. All rights reserved.
  4. //
  5. // FILE
  6. //
  7. // timerq.cpp
  8. //
  9. // SYNOPSIS
  10. //
  11. // Defines the class TimerQueue.
  12. //
  13. // MODIFICATION HISTORY
  14. //
  15. // 02/10/2000 Original version.
  16. //
  17. ///////////////////////////////////////////////////////////////////////////////
  18. #include <proxypch.h>
  19. #include <timerq.h>
  20. void Timer::cancelTimer() throw ()
  21. {
  22. TimerQueue* ourQueue = queue;
  23. if (ourQueue)
  24. {
  25. ourQueue->cancelTimer(this);
  26. }
  27. }
  28. TimerQueue::TimerQueue()
  29. : idle(TRUE), hasWatcher(false)
  30. {
  31. CallbackRoutine = startRoutine;
  32. // We bump up the use count to prevent the idle event from being
  33. // continuously set and reset.
  34. ++useCount;
  35. }
  36. TimerQueue::~TimerQueue() throw ()
  37. {
  38. cancelAllTimers();
  39. }
  40. inline void TimerQueue::createWatcher() throw ()
  41. {
  42. if (IASRequestThread(this)) { hasWatcher = TRUE; }
  43. }
  44. bool TimerQueue::setTimer(
  45. Timer* timer,
  46. ULONG dueTime,
  47. ULONG period
  48. ) throw ()
  49. {
  50. // Make sure it's not already set.
  51. timer->cancelTimer();
  52. // Set the timer's period.
  53. timer->period = period;
  54. monitor.lock();
  55. // Add it to the queue ...
  56. bool success = queueTimer(timer, dueTime);
  57. // .. and ensure we have someone to watch it.
  58. if (success && !hasWatcher) { createWatcher(); }
  59. monitor.unlock();
  60. return success;
  61. }
  62. void TimerQueue::cancelAllTimers() throw ()
  63. {
  64. monitor.lock();
  65. // Release the references.
  66. for (TimerIterator i = queue.begin(); i != queue.end(); ++i)
  67. {
  68. i->second->queue = NULL;
  69. i->second->Release();
  70. }
  71. // Clear the queue.
  72. queue.clear();
  73. // Allow the use count to go to zero, so the idle event will be set.
  74. if (--useCount == 0) { idle.set(); }
  75. monitor.unlock();
  76. // Wake up the watcher, so he'll see the queue is empty.
  77. nudge.set();
  78. // Wait until we're idle, ...
  79. idle.wait();
  80. // ... then put the useCount back up.
  81. ++useCount;
  82. }
  83. void TimerQueue::cancelTimer(Timer* timer) throw ()
  84. {
  85. monitor.lock();
  86. // Make sure this is from the right queue. We checked this is
  87. // Timer::cancelTimer, but we need to do it again now that we hold the lock.
  88. if (timer->queue == this)
  89. {
  90. // Remove it from the queue.
  91. queue.erase(timer->self);
  92. timer->queue = NULL;
  93. // Release the reference we added in queueTimer.
  94. timer->Release();
  95. // Normally, we won't bother to wake the watcher, but if this was the last
  96. // timer, we'll let him exit.
  97. if (queue.empty()) { nudge.set(); }
  98. }
  99. monitor.unlock();
  100. }
  101. void TimerQueue::executeOneTimer() throw ()
  102. {
  103. // The timer to be executed.
  104. Timer* timer = NULL;
  105. monitor.lock();
  106. // Loop until either we get a timer or the queue is empty.
  107. while (!queue.empty())
  108. {
  109. ULONG64 now = GetSystemTime64();
  110. // Has the next timer expired ?
  111. if (now >= queue.begin()->first)
  112. {
  113. // Yes, so save it for later.
  114. timer = queue.begin()->second;
  115. // Remove it from the queue.
  116. queue.erase(queue.begin());
  117. timer->queue = NULL;
  118. // If it's recurrent, set it again.
  119. if (timer->period) { queueTimer(timer, timer->period); }
  120. break;
  121. }
  122. // Calculate the time until the next timer expires ...
  123. ULONG timeout = (queue.begin()->first - now) / 10000;
  124. monitor.unlock();
  125. // ... and wait.
  126. nudge.wait(timeout);
  127. monitor.lock();
  128. }
  129. // We're not watching the queue anymore.
  130. hasWatcher = false;
  131. // Do we need a replacement?
  132. if (!queue.empty()) { createWatcher(); }
  133. monitor.unlock();
  134. if (timer)
  135. {
  136. // Invoke the callback.
  137. timer->onExpiry();
  138. // Release the reference we added in queueTimer.
  139. timer->Release();
  140. }
  141. // We're exiting, so drop the useCount.
  142. if (--useCount == 0) { idle.set(); }
  143. }
  144. bool TimerQueue::queueTimer(Timer* timer, ULONG dueTime) throw ()
  145. {
  146. // NOTE: You must hold the lock when calling this method.
  147. ULONG64 expiry = GetSystemTime64() + dueTime * 10000i64;
  148. bool success;
  149. try
  150. {
  151. // Insert the timer into the queue.
  152. timer->self = queue.insert(Timers::value_type(expiry, timer));
  153. // If this is the next timer to expire, we need to nudge the watcher.
  154. if (timer->self == queue.begin()) { nudge.set(); }
  155. timer->queue = this;
  156. timer->AddRef();
  157. success = true;
  158. }
  159. catch (...)
  160. {
  161. success = false;
  162. }
  163. return success;
  164. }
  165. void TimerQueue::startRoutine(PIAS_CALLBACK This) throw ()
  166. {
  167. static_cast<TimerQueue*>(This)->executeOneTimer();
  168. }