OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright 2016 The WebRTC Project Authors. All rights reserved. | 2 * Copyright 2016 The WebRTC Project Authors. All rights reserved. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license | 4 * Use of this source code is governed by a BSD-style license |
5 * that can be found in the LICENSE file in the root of the source | 5 * that can be found in the LICENSE file in the root of the source |
6 * tree. An additional intellectual property rights grant can be found | 6 * tree. An additional intellectual property rights grant can be found |
7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
9 */ | 9 */ |
10 | 10 |
11 #include "webrtc/base/task_queue.h" | 11 #include "webrtc/base/task_queue.h" |
12 | 12 |
13 #include <mmsystem.h> | 13 #include <mmsystem.h> |
14 #include <string.h> | 14 #include <string.h> |
15 | 15 |
16 #include <algorithm> | 16 #include <queue> |
17 | 17 |
18 #include "webrtc/base/arraysize.h" | 18 #include "webrtc/base/arraysize.h" |
19 #include "webrtc/base/checks.h" | 19 #include "webrtc/base/checks.h" |
20 #include "webrtc/base/logging.h" | 20 #include "webrtc/base/logging.h" |
21 #include "webrtc/base/timeutils.h" | |
21 | 22 |
22 namespace rtc { | 23 namespace rtc { |
23 namespace { | 24 namespace { |
24 #define WM_RUN_TASK WM_USER + 1 | 25 #define WM_RUN_TASK WM_USER + 1 |
25 #define WM_QUEUE_DELAYED_TASK WM_USER + 2 | 26 #define WM_QUEUE_DELAYED_TASK WM_USER + 2 |
26 | 27 |
27 using Priority = TaskQueue::Priority; | 28 using Priority = TaskQueue::Priority; |
28 | 29 |
29 DWORD g_queue_ptr_tls = 0; | 30 DWORD g_queue_ptr_tls = 0; |
30 | 31 |
(...skipping 29 matching lines...) Expand all Loading... | |
60 return kLowPriority; | 61 return kLowPriority; |
61 case Priority::NORMAL: | 62 case Priority::NORMAL: |
62 return kNormalPriority; | 63 return kNormalPriority; |
63 default: | 64 default: |
64 RTC_NOTREACHED(); | 65 RTC_NOTREACHED(); |
65 break; | 66 break; |
66 } | 67 } |
67 return kNormalPriority; | 68 return kNormalPriority; |
68 } | 69 } |
69 | 70 |
70 #if defined(_WIN64) | 71 int64_t GetTick() { |
71 DWORD GetTick() { | |
72 static const UINT kPeriod = 1; | 72 static const UINT kPeriod = 1; |
73 bool high_res = (timeBeginPeriod(kPeriod) == TIMERR_NOERROR); | 73 bool high_res = (timeBeginPeriod(kPeriod) == TIMERR_NOERROR); |
74 DWORD ret = timeGetTime(); | 74 int64_t ret = TimeMillis(); |
75 if (high_res) | 75 if (high_res) |
76 timeEndPeriod(kPeriod); | 76 timeEndPeriod(kPeriod); |
77 return ret; | 77 return ret; |
78 } | 78 } |
79 #endif | |
80 } // namespace | |
81 | 79 |
82 class TaskQueue::MultimediaTimer { | 80 class DelayedTaskInfo { |
83 public: | 81 public: |
84 // kMaxTimers defines the limit of how many MultimediaTimer instances should | 82 // Default ctor needed to support priority_queue::pop(). |
85 // be created. | 83 DelayedTaskInfo() {} |
86 // Background: The maximum number of supported handles for Wait functions, is | 84 DelayedTaskInfo(uint32_t milliseconds, std::unique_ptr<QueuedTask> task) |
87 // MAXIMUM_WAIT_OBJECTS - 1 (63). | 85 : due_time_(GetTick() + milliseconds), task_(std::move(task)) {} |
88 // There are some ways to work around the limitation but as it turns out, the | 86 DelayedTaskInfo(DelayedTaskInfo&&) = default; |
89 // limit of concurrently active multimedia timers per process, is much lower, | |
90 // or 16. So there isn't much value in going to the lenghts required to | |
91 // overcome the Wait limitations. | |
92 // kMaxTimers is larger than 16 though since it is possible that 'complete' or | |
93 // signaled timers that haven't been handled, are counted as part of | |
94 // kMaxTimers and thus a multimedia timer can actually be queued even though | |
95 // as far as we're concerned, there are more than 16 that are pending. | |
96 static const int kMaxTimers = MAXIMUM_WAIT_OBJECTS - 1; | |
97 | 87 |
98 // Controls how many MultimediaTimer instances a queue can hold before | 88 // Implement for <set> to maintain an order of increasing |due_time_|. |
the sun
2017/03/10 11:48:02
nit: <set> not correct anymore
tommi
2017/03/10 15:58:50
Done.
| |
99 // attempting to garbage collect (GC) timers that aren't in use. | 89 bool operator<(const DelayedTaskInfo& other) const { |
100 static const int kInstanceThresholdGC = 8; | 90 return due_time_ < other.due_time_; |
91 } | |
101 | 92 |
93 // Required by priority_queue::pop(). | |
94 DelayedTaskInfo& operator=(DelayedTaskInfo&& other) = default; | |
95 | |
96 // See below for why this method is const. | |
97 void Run() const { | |
98 RTC_DCHECK(due_time_); | |
99 task_->Run() ? task_.reset() : static_cast<void>(task_.release()); | |
100 } | |
101 | |
102 int64_t due_time() const { return due_time_; } | |
103 | |
104 private: | |
105 int64_t due_time_ = 0; // Absolute timestamp in milliseconds. | |
106 | |
107 // |task| needs to be mutable because std::priority_queue::top() returns | |
108 // a const reference and a key in an ordered queue must not be changed. | |
109 // There are two basic workarounds, one using const_cast, which would also | |
110 // make the key (|due_time|), non-const and the other is to make the non-key | |
111 // (|task|), mutable. | |
112 // Because of this, the |task| variable is made private and can only be | |
113 // mutated by calling the |Run()| method. | |
114 mutable std::unique_ptr<QueuedTask> task_; | |
115 }; | |
116 | |
117 class MultimediaTimer { | |
118 public: | |
102 MultimediaTimer() : event_(::CreateEvent(nullptr, false, false, nullptr)) {} | 119 MultimediaTimer() : event_(::CreateEvent(nullptr, false, false, nullptr)) {} |
103 | 120 |
104 MultimediaTimer(MultimediaTimer&& timer) | 121 ~MultimediaTimer() { |
105 : event_(timer.event_), | 122 Cancel(); |
106 timer_id_(timer.timer_id_), | 123 ::CloseHandle(event_); |
107 task_(std::move(timer.task_)) { | |
108 RTC_DCHECK(event_); | |
109 timer.event_ = nullptr; | |
110 timer.timer_id_ = 0; | |
111 } | 124 } |
112 | 125 |
113 ~MultimediaTimer() { Close(); } | 126 bool StartOneShotTimer(UINT delay_ms) { |
114 | |
115 // Implementing this operator is required because of the way | |
116 // some stl algorithms work, such as std::rotate(). | |
117 MultimediaTimer& operator=(MultimediaTimer&& timer) { | |
118 if (this != &timer) { | |
119 Close(); | |
120 event_ = timer.event_; | |
121 timer.event_ = nullptr; | |
122 task_ = std::move(timer.task_); | |
123 timer_id_ = timer.timer_id_; | |
124 timer.timer_id_ = 0; | |
125 } | |
126 return *this; | |
127 } | |
128 | |
129 bool StartOneShotTimer(std::unique_ptr<QueuedTask> task, UINT delay_ms) { | |
130 RTC_DCHECK_EQ(0, timer_id_); | 127 RTC_DCHECK_EQ(0, timer_id_); |
131 RTC_DCHECK(event_ != nullptr); | 128 RTC_DCHECK(event_ != nullptr); |
132 RTC_DCHECK(!task_.get()); | |
133 RTC_DCHECK(task.get()); | |
134 task_ = std::move(task); | |
135 timer_id_ = | 129 timer_id_ = |
136 ::timeSetEvent(delay_ms, 0, reinterpret_cast<LPTIMECALLBACK>(event_), 0, | 130 ::timeSetEvent(delay_ms, 0, reinterpret_cast<LPTIMECALLBACK>(event_), 0, |
137 TIME_ONESHOT | TIME_CALLBACK_EVENT_SET); | 131 TIME_ONESHOT | TIME_CALLBACK_EVENT_SET); |
138 return timer_id_ != 0; | 132 return timer_id_ != 0; |
139 } | 133 } |
140 | 134 |
141 std::unique_ptr<QueuedTask> Cancel() { | 135 void Cancel() { |
142 if (timer_id_) { | 136 if (timer_id_) { |
143 ::timeKillEvent(timer_id_); | 137 ::timeKillEvent(timer_id_); |
144 timer_id_ = 0; | 138 timer_id_ = 0; |
145 } | 139 } |
146 return std::move(task_); | |
147 } | 140 } |
148 | 141 |
149 void OnEventSignaled() { | 142 HANDLE* event_for_wait() { return &event_; } |
150 RTC_DCHECK_NE(0, timer_id_); | |
151 timer_id_ = 0; | |
152 task_->Run() ? task_.reset() : static_cast<void>(task_.release()); | |
153 } | |
154 | |
155 HANDLE event() const { return event_; } | |
156 | |
157 bool is_active() const { return timer_id_ != 0; } | |
158 | 143 |
159 private: | 144 private: |
160 void Close() { | |
161 Cancel(); | |
162 | |
163 if (event_) { | |
164 ::CloseHandle(event_); | |
165 event_ = nullptr; | |
166 } | |
167 } | |
168 | |
169 HANDLE event_ = nullptr; | 145 HANDLE event_ = nullptr; |
170 MMRESULT timer_id_ = 0; | 146 MMRESULT timer_id_ = 0; |
171 std::unique_ptr<QueuedTask> task_; | |
172 | 147 |
173 RTC_DISALLOW_COPY_AND_ASSIGN(MultimediaTimer); | 148 RTC_DISALLOW_COPY_AND_ASSIGN(MultimediaTimer); |
174 }; | 149 }; |
175 | 150 |
151 } // namespace | |
152 | |
153 class TaskQueue::ThreadState { | |
154 public: | |
155 ThreadState() {} | |
156 ~ThreadState() {} | |
157 | |
158 void RunThreadMain(); | |
159 | |
160 private: | |
161 bool ProcessQueuedMessages(); | |
162 void RunDueTasks(); | |
163 void ScheduleNextTimer(); | |
164 void CancelTimers(); | |
165 | |
166 MultimediaTimer timer_; | |
167 std::priority_queue<DelayedTaskInfo> timer_tasks_; | |
168 UINT_PTR timer_id_ = 0; | |
169 }; | |
170 | |
176 TaskQueue::TaskQueue(const char* queue_name, Priority priority /*= NORMAL*/) | 171 TaskQueue::TaskQueue(const char* queue_name, Priority priority /*= NORMAL*/) |
177 : thread_(&TaskQueue::ThreadMain, | 172 : thread_(&TaskQueue::ThreadMain, |
178 this, | 173 this, |
179 queue_name, | 174 queue_name, |
180 TaskQueuePriorityToThreadPriority(priority)) { | 175 TaskQueuePriorityToThreadPriority(priority)) { |
181 RTC_DCHECK(queue_name); | 176 RTC_DCHECK(queue_name); |
182 thread_.Start(); | 177 thread_.Start(); |
183 Event event(false, false); | 178 Event event(false, false); |
184 ThreadStartupData startup = {&event, this}; | 179 ThreadStartupData startup = {&event, this}; |
185 RTC_CHECK(thread_.QueueAPC(&InitializeQueueThread, | 180 RTC_CHECK(thread_.QueueAPC(&InitializeQueueThread, |
(...skipping 27 matching lines...) Expand all Loading... | |
213 | 208 |
214 void TaskQueue::PostTask(std::unique_ptr<QueuedTask> task) { | 209 void TaskQueue::PostTask(std::unique_ptr<QueuedTask> task) { |
215 if (::PostThreadMessage(thread_.GetThreadRef(), WM_RUN_TASK, 0, | 210 if (::PostThreadMessage(thread_.GetThreadRef(), WM_RUN_TASK, 0, |
216 reinterpret_cast<LPARAM>(task.get()))) { | 211 reinterpret_cast<LPARAM>(task.get()))) { |
217 task.release(); | 212 task.release(); |
218 } | 213 } |
219 } | 214 } |
220 | 215 |
221 void TaskQueue::PostDelayedTask(std::unique_ptr<QueuedTask> task, | 216 void TaskQueue::PostDelayedTask(std::unique_ptr<QueuedTask> task, |
222 uint32_t milliseconds) { | 217 uint32_t milliseconds) { |
223 WPARAM wparam; | 218 if (!milliseconds) { |
224 #if defined(_WIN64) | 219 PostTask(std::move(task)); |
225 // GetTickCount() returns a fairly coarse tick count (resolution or about 8ms) | 220 return; |
226 // so this compensation isn't that accurate, but since we have unused 32 bits | 221 } |
227 // on Win64, we might as well use them. | 222 |
228 wparam = (static_cast<WPARAM>(GetTick()) << 32) | milliseconds; | 223 // TODO(tommi): Avoid this allocation. It is currently here since |
229 #else | 224 // the timestamp stored in the task info object, is a 64bit timestamp |
230 wparam = milliseconds; | 225 // and WPARAM is 32bits in 32bit builds. Otherwise, we could pass the |
231 #endif | 226 // task pointer and timestamp as LPARAM and WPARAM. |
232 if (::PostThreadMessage(thread_.GetThreadRef(), WM_QUEUE_DELAYED_TASK, wparam, | 227 auto* task_info = new DelayedTaskInfo(milliseconds, std::move(task)); |
233 reinterpret_cast<LPARAM>(task.get()))) { | 228 if (!::PostThreadMessage(thread_.GetThreadRef(), WM_QUEUE_DELAYED_TASK, 0, |
234 task.release(); | 229 reinterpret_cast<LPARAM>(task_info))) { |
230 delete task_info; | |
235 } | 231 } |
236 } | 232 } |
237 | 233 |
238 void TaskQueue::PostTaskAndReply(std::unique_ptr<QueuedTask> task, | 234 void TaskQueue::PostTaskAndReply(std::unique_ptr<QueuedTask> task, |
239 std::unique_ptr<QueuedTask> reply, | 235 std::unique_ptr<QueuedTask> reply, |
240 TaskQueue* reply_queue) { | 236 TaskQueue* reply_queue) { |
241 QueuedTask* task_ptr = task.release(); | 237 QueuedTask* task_ptr = task.release(); |
242 QueuedTask* reply_task_ptr = reply.release(); | 238 QueuedTask* reply_task_ptr = reply.release(); |
243 DWORD reply_thread_id = reply_queue->thread_.GetThreadRef(); | 239 DWORD reply_thread_id = reply_queue->thread_.GetThreadRef(); |
244 PostTask([task_ptr, reply_task_ptr, reply_thread_id]() { | 240 PostTask([task_ptr, reply_task_ptr, reply_thread_id]() { |
245 if (task_ptr->Run()) | 241 if (task_ptr->Run()) |
246 delete task_ptr; | 242 delete task_ptr; |
247 // If the thread's message queue is full, we can't queue the task and will | 243 // If the thread's message queue is full, we can't queue the task and will |
248 // have to drop it (i.e. delete). | 244 // have to drop it (i.e. delete). |
249 if (!::PostThreadMessage(reply_thread_id, WM_RUN_TASK, 0, | 245 if (!::PostThreadMessage(reply_thread_id, WM_RUN_TASK, 0, |
250 reinterpret_cast<LPARAM>(reply_task_ptr))) { | 246 reinterpret_cast<LPARAM>(reply_task_ptr))) { |
251 delete reply_task_ptr; | 247 delete reply_task_ptr; |
252 } | 248 } |
253 }); | 249 }); |
254 } | 250 } |
255 | 251 |
256 void TaskQueue::PostTaskAndReply(std::unique_ptr<QueuedTask> task, | 252 void TaskQueue::PostTaskAndReply(std::unique_ptr<QueuedTask> task, |
257 std::unique_ptr<QueuedTask> reply) { | 253 std::unique_ptr<QueuedTask> reply) { |
258 return PostTaskAndReply(std::move(task), std::move(reply), Current()); | 254 return PostTaskAndReply(std::move(task), std::move(reply), Current()); |
259 } | 255 } |
260 | 256 |
261 // static | 257 // static |
262 void TaskQueue::ThreadMain(void* context) { | 258 void TaskQueue::ThreadMain(void* context) { |
263 HANDLE timer_handles[MultimediaTimer::kMaxTimers]; | 259 ThreadState state; |
264 // Active multimedia timers. | 260 state.RunThreadMain(); |
265 std::vector<MultimediaTimer> mm_timers; | 261 } |
266 // Tasks that have been queued by using SetTimer/WM_TIMER. | |
267 DelayedTasks delayed_tasks; | |
268 | 262 |
263 void TaskQueue::ThreadState::RunThreadMain() { | |
269 while (true) { | 264 while (true) { |
270 RTC_DCHECK(mm_timers.size() <= arraysize(timer_handles)); | |
271 DWORD count = 0; | |
272 for (const auto& t : mm_timers) { | |
273 if (!t.is_active()) | |
274 break; | |
275 timer_handles[count++] = t.event(); | |
276 } | |
277 // Make sure we do an alertable wait as that's required to allow APCs to run | 265 // Make sure we do an alertable wait as that's required to allow APCs to run |
278 // (e.g. required for InitializeQueueThread and stopping the thread in | 266 // (e.g. required for InitializeQueueThread and stopping the thread in |
279 // PlatformThread). | 267 // PlatformThread). |
280 DWORD result = ::MsgWaitForMultipleObjectsEx(count, timer_handles, INFINITE, | 268 DWORD result = ::MsgWaitForMultipleObjectsEx( |
281 QS_ALLEVENTS, MWMO_ALERTABLE); | 269 1, timer_.event_for_wait(), INFINITE, QS_ALLEVENTS, MWMO_ALERTABLE); |
282 RTC_CHECK_NE(WAIT_FAILED, result); | 270 RTC_CHECK_NE(WAIT_FAILED, result); |
283 // If we're not waiting for any timers, then count will be equal to | 271 if (result == (WAIT_OBJECT_0 + 1)) { |
284 // WAIT_OBJECT_0. If we're waiting for timers, then |count| represents | 272 // There are messages in the message queue that need to be handled. |
285 // "One more than the number of timers", which means that there's a | 273 if (!ProcessQueuedMessages()) |
286 // message in the queue that needs to be handled. | |
287 // If |result| is less than |count|, then its value will be the index of the | |
288 // timer that has been signaled. | |
289 if (result == (WAIT_OBJECT_0 + count)) { | |
290 if (!ProcessQueuedMessages(&delayed_tasks, &mm_timers)) | |
291 break; | 274 break; |
292 } else if (result < (WAIT_OBJECT_0 + count)) { | 275 } else if (result == WAIT_OBJECT_0) { |
293 mm_timers[result].OnEventSignaled(); | 276 // The multimedia timer was signaled. |
294 RTC_DCHECK(!mm_timers[result].is_active()); | 277 timer_.Cancel(); |
295 // Reuse timer events by moving inactive timers to the back of the vector. | 278 RTC_DCHECK(!timer_tasks_.empty()); |
296 // When new delayed tasks are queued, they'll get reused. | 279 RunDueTasks(); |
297 if (mm_timers.size() > 1) { | 280 ScheduleNextTimer(); |
298 auto it = mm_timers.begin() + result; | |
299 std::rotate(it, it + 1, mm_timers.end()); | |
300 } | |
301 | |
302 // Collect some garbage. | |
303 if (mm_timers.size() > MultimediaTimer::kInstanceThresholdGC) { | |
304 const auto inactive = std::find_if( | |
305 mm_timers.begin(), mm_timers.end(), | |
306 [](const MultimediaTimer& t) { return !t.is_active(); }); | |
307 if (inactive != mm_timers.end()) { | |
308 // Since inactive timers are always moved to the back, we can | |
309 // safely delete all timers following the first inactive one. | |
310 mm_timers.erase(inactive, mm_timers.end()); | |
311 } | |
312 } | |
313 } else { | 281 } else { |
314 RTC_DCHECK_EQ(WAIT_IO_COMPLETION, result); | 282 RTC_DCHECK_EQ(WAIT_IO_COMPLETION, result); |
315 } | 283 } |
316 } | 284 } |
317 } | 285 } |
318 | 286 |
319 // static | 287 bool TaskQueue::ThreadState::ProcessQueuedMessages() { |
320 bool TaskQueue::ProcessQueuedMessages(DelayedTasks* delayed_tasks, | |
321 std::vector<MultimediaTimer>* timers) { | |
322 MSG msg = {}; | 288 MSG msg = {}; |
323 while (::PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE) && | 289 while (::PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE) && |
324 msg.message != WM_QUIT) { | 290 msg.message != WM_QUIT) { |
325 if (!msg.hwnd) { | 291 if (!msg.hwnd) { |
326 switch (msg.message) { | 292 switch (msg.message) { |
327 case WM_RUN_TASK: { | 293 case WM_RUN_TASK: { |
328 QueuedTask* task = reinterpret_cast<QueuedTask*>(msg.lParam); | 294 QueuedTask* task = reinterpret_cast<QueuedTask*>(msg.lParam); |
329 if (task->Run()) | 295 if (task->Run()) |
330 delete task; | 296 delete task; |
331 break; | 297 break; |
332 } | 298 } |
333 case WM_QUEUE_DELAYED_TASK: { | 299 case WM_QUEUE_DELAYED_TASK: { |
334 std::unique_ptr<QueuedTask> task( | 300 std::unique_ptr<DelayedTaskInfo> info( |
335 reinterpret_cast<QueuedTask*>(msg.lParam)); | 301 reinterpret_cast<DelayedTaskInfo*>(msg.lParam)); |
336 uint32_t milliseconds = msg.wParam & 0xFFFFFFFF; | 302 bool need_to_schedule_timers = timer_tasks_.empty() || |
337 #if defined(_WIN64) | 303 timer_tasks_.top().due_time() > info->due_time(); |
338 // Subtract the time it took to queue the timer. | 304 timer_tasks_.emplace(std::move(*info.get())); |
339 const DWORD now = GetTick(); | 305 if (need_to_schedule_timers) { |
340 DWORD post_time = now - (msg.wParam >> 32); | 306 CancelTimers(); |
341 milliseconds = | 307 ScheduleNextTimer(); |
342 post_time > milliseconds ? 0 : milliseconds - post_time; | |
343 #endif | |
344 bool timer_queued = false; | |
345 if (timers->size() < MultimediaTimer::kMaxTimers) { | |
346 MultimediaTimer* timer = nullptr; | |
347 auto available = std::find_if( | |
348 timers->begin(), timers->end(), | |
349 [](const MultimediaTimer& t) { return !t.is_active(); }); | |
350 if (available != timers->end()) { | |
351 timer = &(*available); | |
352 } else { | |
353 timers->emplace_back(); | |
354 timer = &timers->back(); | |
355 } | |
356 | |
357 timer_queued = | |
358 timer->StartOneShotTimer(std::move(task), milliseconds); | |
359 if (!timer_queued) { | |
360 // No more multimedia timers can be queued. | |
361 // Detach the task and fall back on SetTimer. | |
362 task = timer->Cancel(); | |
363 } | |
364 } | |
365 | |
366 // When we fail to use multimedia timers, we fall back on the more | |
367 // coarse SetTimer/WM_TIMER approach. | |
368 if (!timer_queued) { | |
369 UINT_PTR timer_id = ::SetTimer(nullptr, 0, milliseconds, nullptr); | |
370 delayed_tasks->insert(std::make_pair(timer_id, task.release())); | |
371 } | 308 } |
372 break; | 309 break; |
373 } | 310 } |
374 case WM_TIMER: { | 311 case WM_TIMER: { |
312 RTC_DCHECK_EQ(timer_id_, msg.wParam); | |
375 ::KillTimer(nullptr, msg.wParam); | 313 ::KillTimer(nullptr, msg.wParam); |
376 auto found = delayed_tasks->find(msg.wParam); | 314 timer_id_ = 0; |
377 RTC_DCHECK(found != delayed_tasks->end()); | 315 RunDueTasks(); |
378 if (!found->second->Run()) | 316 ScheduleNextTimer(); |
379 found->second.release(); | |
380 delayed_tasks->erase(found); | |
381 break; | 317 break; |
382 } | 318 } |
383 default: | 319 default: |
384 RTC_NOTREACHED(); | 320 RTC_NOTREACHED(); |
385 break; | 321 break; |
386 } | 322 } |
387 } else { | 323 } else { |
388 ::TranslateMessage(&msg); | 324 ::TranslateMessage(&msg); |
389 ::DispatchMessage(&msg); | 325 ::DispatchMessage(&msg); |
390 } | 326 } |
391 } | 327 } |
392 return msg.message != WM_QUIT; | 328 return msg.message != WM_QUIT; |
393 } | 329 } |
394 | 330 |
331 void TaskQueue::ThreadState::RunDueTasks() { | |
332 RTC_DCHECK(!timer_tasks_.empty()); | |
333 auto now = GetTick(); | |
334 do { | |
335 const auto& top = timer_tasks_.top(); | |
336 if (top.due_time() > now) | |
the sun
2017/03/10 11:48:02
Is it better to check against GetTick() here, in c
tommi
2017/03/10 15:58:50
The thinking in the previous patch set was exactly
| |
337 break; | |
338 top.Run(); | |
339 timer_tasks_.pop(); | |
340 } while (!timer_tasks_.empty()); | |
341 } | |
342 | |
343 void TaskQueue::ThreadState::ScheduleNextTimer() { | |
344 RTC_DCHECK_EQ(timer_id_, 0); | |
345 if (timer_tasks_.empty()) | |
346 return; | |
347 | |
348 auto now = GetTick(); | |
the sun
2017/03/10 11:48:02
nit:
int64_t delay_ms = std::max(0, timer_tasks.to
tommi
2017/03/10 15:58:50
Done.
| |
349 | |
350 const auto& next_task = timer_tasks_.top(); | |
351 uint32_t milliseconds = next_task.due_time() <= now ? 0u : | |
352 static_cast<uint32_t>(next_task.due_time() - now); | |
353 if (!timer_.StartOneShotTimer(milliseconds)) | |
354 timer_id_ = ::SetTimer(nullptr, 0, milliseconds, nullptr); | |
355 } | |
356 | |
357 void TaskQueue::ThreadState::CancelTimers() { | |
358 timer_.Cancel(); | |
359 if (timer_id_) { | |
360 ::KillTimer(nullptr, timer_id_); | |
361 timer_id_ = 0; | |
362 } | |
363 } | |
364 | |
395 } // namespace rtc | 365 } // namespace rtc |
OLD | NEW |