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 |