| OLD | NEW |
| (Empty) |
| 1 // Copyright 2007-2009 Google Inc. | |
| 2 // | |
| 3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
| 4 // you may not use this file except in compliance with the License. | |
| 5 // You may obtain a copy of the License at | |
| 6 // | |
| 7 // http://www.apache.org/licenses/LICENSE-2.0 | |
| 8 // | |
| 9 // Unless required by applicable law or agreed to in writing, software | |
| 10 // distributed under the License is distributed on an "AS IS" BASIS, | |
| 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 12 // See the License for the specific language governing permissions and | |
| 13 // limitations under the License. | |
| 14 // ======================================================================== | |
| 15 // | |
| 16 // The implementation is straightforward except the destruction of the | |
| 17 // QueueTimer which needs some clarification. | |
| 18 // If there is no callback running, then the destructor gets the critical | |
| 19 // section and then it blocks on the DeleteTimerQueueTimer call, waiting for | |
| 20 // the kernel to clean up the timer handle. The callback never fires in this | |
| 21 // case. | |
| 22 // If a callback is running, then there are two possibilities: | |
| 23 // 1. The callback gets the critical section. The callback runs as usual and | |
| 24 // then the destructor gets the critical section. This is also easy. | |
| 25 // 2. The destructor gets the critical section. In this case, the callback | |
| 26 // tries the critical section then it returns right away. | |
| 27 // | |
| 28 // Alarm timers are started and restarted every time they fire. The usage | |
| 29 // patterns for alarms is usually Start, Callback, Start, Callback, etc... | |
| 30 // The cleanup of an alarm timer handle usually happens in the callback, unless | |
| 31 // the destructor of the QueueTimer is called, in which case the logic | |
| 32 // above applies. | |
| 33 // | |
| 34 // Periodic timers are only started once: Start, Callback, Callback, etc... | |
| 35 // In this case, the destructor does all the necessary cleanup. | |
| 36 // Periodic timers must fire at intervals that are reasonable long so that | |
| 37 // the callbacks do not queue up. | |
| 38 | |
| 39 #include "omaha/base/queue_timer.h" | |
| 40 | |
| 41 #include "omaha/base/debug.h" | |
| 42 #include "omaha/base/error.h" | |
| 43 #include "omaha/base/logging.h" | |
| 44 | |
| 45 namespace omaha { | |
| 46 | |
| 47 QueueTimer::QueueTimer(HANDLE timer_queue, Callback callback, void* ctx) | |
| 48 : callback_tid_(0), | |
| 49 ctx_(ctx), | |
| 50 due_time_(0), | |
| 51 period_(0), | |
| 52 flags_(0), | |
| 53 timer_handle_(NULL), | |
| 54 timer_queue_(timer_queue), | |
| 55 callback_(callback) { | |
| 56 UTIL_LOG(L3, (_T("[QueueTimer::QueueTimer][0x%p]"), this)); | |
| 57 ASSERT1(timer_queue); | |
| 58 ASSERT1(callback); | |
| 59 ::InitializeCriticalSection(&dtor_cs_); | |
| 60 ::InitializeCriticalSection(&cs_); | |
| 61 } | |
| 62 | |
| 63 // The destructor blocks on waiting for the timer kernel object to be deleted. | |
| 64 // We can't call the destructor of QueueTimer while we are handling a callback. | |
| 65 // This will result is a deadlock. | |
| 66 QueueTimer::~QueueTimer() { | |
| 67 UTIL_LOG(L3, (_T("[QueueTimer::~QueueTimer][0x%p]"), this)); | |
| 68 | |
| 69 ::EnterCriticalSection(&dtor_cs_); | |
| 70 if (timer_handle_) { | |
| 71 ASSERT1(callback_tid_ != ::GetCurrentThreadId()); | |
| 72 | |
| 73 // This is a blocking call waiting for all callbacks to clear up. | |
| 74 bool res = !!::DeleteTimerQueueTimer(timer_queue_, | |
| 75 timer_handle_, | |
| 76 INVALID_HANDLE_VALUE); | |
| 77 ASSERT1(res); | |
| 78 timer_handle_ = NULL; | |
| 79 } | |
| 80 callback_ = NULL; | |
| 81 timer_queue_ = NULL; | |
| 82 flags_ = 0; | |
| 83 period_ = 0; | |
| 84 due_time_ = 0; | |
| 85 ctx_ = 0; | |
| 86 callback_tid_ = 0; | |
| 87 ::LeaveCriticalSection(&dtor_cs_); | |
| 88 | |
| 89 ::DeleteCriticalSection(&cs_); | |
| 90 ::DeleteCriticalSection(&dtor_cs_); | |
| 91 } | |
| 92 | |
| 93 // Thread safe. | |
| 94 HRESULT QueueTimer::Start(int due_time, int period, uint32 flags) { | |
| 95 // Since Start creates the timer there could be a race condition where | |
| 96 // the timer could fire while we are still executing Start. We protect | |
| 97 // the start with a critical section so the Start completes before the | |
| 98 // timer can be entered by the callback. | |
| 99 | |
| 100 ::EnterCriticalSection(&cs_); | |
| 101 HRESULT hr = DoStart(due_time, period, flags); | |
| 102 ::LeaveCriticalSection(&cs_); | |
| 103 return hr; | |
| 104 } | |
| 105 | |
| 106 // Thread-safe. | |
| 107 void QueueTimer::TimerCallback(void* param, BOOLEAN timer_or_wait) { | |
| 108 ASSERT1(param); | |
| 109 VERIFY1(timer_or_wait); | |
| 110 | |
| 111 QueueTimer* timer = static_cast<QueueTimer*>(param); | |
| 112 | |
| 113 if (!::TryEnterCriticalSection(&timer->dtor_cs_)) { | |
| 114 return; | |
| 115 } | |
| 116 | |
| 117 ::EnterCriticalSection(&timer->cs_); | |
| 118 timer->DoCallback(); | |
| 119 ::LeaveCriticalSection(&timer->cs_); | |
| 120 | |
| 121 ::LeaveCriticalSection(&timer->dtor_cs_); | |
| 122 } | |
| 123 | |
| 124 | |
| 125 HRESULT QueueTimer::DoStart(int due_time, int period, uint32 flags) { | |
| 126 UTIL_LOG(L2, (_T("[QueueTimer::DoStart][0x%p][%d][%d][0x%08u]"), | |
| 127 this, due_time, period, flags)); | |
| 128 due_time_ = due_time; | |
| 129 period_ = period; | |
| 130 flags_ = flags; | |
| 131 | |
| 132 // Application Verifier says period must be 0 for WT_EXECUTEONLYONCE timers. | |
| 133 if ((flags & WT_EXECUTEONLYONCE) && period != 0) { | |
| 134 return E_INVALIDARG; | |
| 135 } | |
| 136 | |
| 137 // Periodic timers can't be started more than one time. | |
| 138 if (timer_handle_) { | |
| 139 return E_UNEXPECTED; | |
| 140 } | |
| 141 | |
| 142 bool res = !!::CreateTimerQueueTimer(&timer_handle_, | |
| 143 timer_queue_, | |
| 144 &QueueTimer::TimerCallback, | |
| 145 this, | |
| 146 due_time, | |
| 147 period, | |
| 148 flags_); | |
| 149 if (!res) { | |
| 150 HRESULT hr = HRESULTFromLastError(); | |
| 151 UTIL_LOG(LE, (_T("[QueueTimer::Start failed][0x%p][0x%08x]"), this, hr)); | |
| 152 return hr; | |
| 153 } | |
| 154 | |
| 155 ASSERT1(timer_handle_); | |
| 156 UTIL_LOG(L3, (_T("[QueueTimer::Start timer created][0x%p]"), this)); | |
| 157 return S_OK; | |
| 158 } | |
| 159 | |
| 160 void QueueTimer::DoCallback() { | |
| 161 UTIL_LOG(L2, (_T("[QueueTimer::OnCallback][0x%p]"), this)); | |
| 162 | |
| 163 ASSERT1(timer_queue_); | |
| 164 ASSERT1(timer_handle_); | |
| 165 ASSERT1(callback_); | |
| 166 | |
| 167 if (!period_) { | |
| 168 // Non-periodic aka alarm timers fire only once. We delete the timer | |
| 169 // handle so that the timer object can be restarted later on. | |
| 170 // The call below is non-blocking. The deletion of the kernel object can | |
| 171 // succeed right away, for example if the timer runs in the timer thread | |
| 172 // itself. Otherwise, if the last error is ERROR_IO_PENDING the kernel | |
| 173 // cleans up the object once the callback returns. | |
| 174 bool res = !!::DeleteTimerQueueTimer(timer_queue_, timer_handle_, NULL); | |
| 175 ASSERT1(res || (!res && ::GetLastError() == ERROR_IO_PENDING)); | |
| 176 timer_handle_ = NULL; | |
| 177 } | |
| 178 | |
| 179 callback_tid_ = ::GetCurrentThreadId(); | |
| 180 callback_(this); | |
| 181 callback_tid_ = 0; | |
| 182 } | |
| 183 | |
| 184 } // namespace omaha | |
| 185 | |
| OLD | NEW |