Chromium Code Reviews| 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 <set> |
| 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 struct DelayedTaskInfo { |
| 83 public: | 81 DelayedTaskInfo(uint32_t milliseconds, std::unique_ptr<QueuedTask> task) |
| 84 // kMaxTimers defines the limit of how many MultimediaTimer instances should | 82 : due_time(GetTick() + milliseconds), task(std::move(task)) {} |
| 85 // be created. | 83 DelayedTaskInfo(const DelayedTaskInfo&) = delete; |
| 86 // Background: The maximum number of supported handles for Wait functions, is | 84 DelayedTaskInfo(DelayedTaskInfo&&) = default; |
|
the sun
2017/03/07 11:24:24
Maybe delete operator= too?
tommi
2017/03/09 20:27:42
Done. (added RTC_DISALLOW_COPY_AND_ASSIGN)
tommi
2017/03/09 20:29:08
oops, discard that. I had to support the operator
| |
| 87 // MAXIMUM_WAIT_OBJECTS - 1 (63). | |
| 88 // There are some ways to work around the limitation but as it turns out, the | |
| 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 | 85 |
| 98 // Controls how many MultimediaTimer instances a queue can hold before | 86 // Implement for <set>. to maintain an order of increasing |due_time|. |
|
the sun
2017/03/07 11:24:24
nit: , after <set>
tommi
2017/03/09 20:27:42
Done.
| |
| 99 // attempting to garbage collect (GC) timers that aren't in use. | 87 bool operator<(const DelayedTaskInfo& other) const { |
| 100 static const int kInstanceThresholdGC = 8; | 88 return due_time < other.due_time; |
| 101 | |
| 102 MultimediaTimer() : event_(::CreateEvent(nullptr, false, false, nullptr)) {} | |
| 103 | |
| 104 MultimediaTimer(MultimediaTimer&& timer) | |
| 105 : event_(timer.event_), | |
| 106 timer_id_(timer.timer_id_), | |
| 107 task_(std::move(timer.task_)) { | |
| 108 RTC_DCHECK(event_); | |
| 109 timer.event_ = nullptr; | |
| 110 timer.timer_id_ = 0; | |
| 111 } | 89 } |
| 112 | 90 |
| 113 ~MultimediaTimer() { Close(); } | 91 // See below for why this method is const. |
| 114 | 92 void Run() const { |
| 115 // Implementing this operator is required because of the way | 93 task->Run() ? task.reset() : static_cast<void>(task.release()); |
| 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 } | 94 } |
| 128 | 95 |
| 129 bool StartOneShotTimer(std::unique_ptr<QueuedTask> task, UINT delay_ms) { | 96 const int64_t due_time; // Absolute timestamp in milliseconds. |
| 97 | |
| 98 private: | |
| 99 // |task| needs to be mutable because of an issue with std::multiset whereby | |
| 100 // set::begin() returns a const_iterator by default. | |
| 101 // There are two basic workarounds, one using const_cast, which would also | |
| 102 // make the key (|due_time|), non-const and the other is to make the non-key | |
| 103 // (|task|), mutable. | |
| 104 // Because of this, the |task| variable is made private and can only be | |
| 105 // mutated by calling the |Run()| method. | |
| 106 mutable std::unique_ptr<QueuedTask> task; | |
| 107 }; | |
| 108 | |
| 109 class MultimediaTimer { | |
| 110 public: | |
| 111 MultimediaTimer() : event_(::CreateEvent(nullptr, false, false, nullptr)) {} | |
| 112 MultimediaTimer(MultimediaTimer&&) = delete; | |
|
the sun
2017/03/07 11:24:24
Strictly speaking I don't think this is needed: ht
tommi
2017/03/09 20:27:42
Removed. I only needed it as I was changing the pr
| |
| 113 | |
| 114 ~MultimediaTimer() { | |
| 115 Cancel(); | |
| 116 ::CloseHandle(event_); | |
| 117 } | |
| 118 | |
| 119 bool StartOneShotTimer(UINT delay_ms) { | |
| 130 RTC_DCHECK_EQ(0, timer_id_); | 120 RTC_DCHECK_EQ(0, timer_id_); |
| 131 RTC_DCHECK(event_ != nullptr); | 121 RTC_DCHECK(event_ != nullptr); |
| 132 RTC_DCHECK(!task_.get()); | |
| 133 RTC_DCHECK(task.get()); | |
| 134 task_ = std::move(task); | |
| 135 timer_id_ = | 122 timer_id_ = |
| 136 ::timeSetEvent(delay_ms, 0, reinterpret_cast<LPTIMECALLBACK>(event_), 0, | 123 ::timeSetEvent(delay_ms, 0, reinterpret_cast<LPTIMECALLBACK>(event_), 0, |
| 137 TIME_ONESHOT | TIME_CALLBACK_EVENT_SET); | 124 TIME_ONESHOT | TIME_CALLBACK_EVENT_SET); |
| 138 return timer_id_ != 0; | 125 return timer_id_ != 0; |
| 139 } | 126 } |
| 140 | 127 |
| 141 std::unique_ptr<QueuedTask> Cancel() { | 128 void Cancel() { |
| 142 if (timer_id_) { | 129 if (timer_id_) { |
| 143 ::timeKillEvent(timer_id_); | 130 ::timeKillEvent(timer_id_); |
| 144 timer_id_ = 0; | 131 timer_id_ = 0; |
| 145 } | 132 } |
| 146 return std::move(task_); | |
| 147 } | 133 } |
| 148 | 134 |
| 149 void OnEventSignaled() { | 135 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 | 136 |
| 159 private: | 137 private: |
| 160 void Close() { | |
| 161 Cancel(); | |
| 162 | |
| 163 if (event_) { | |
| 164 ::CloseHandle(event_); | |
| 165 event_ = nullptr; | |
| 166 } | |
| 167 } | |
| 168 | |
| 169 HANDLE event_ = nullptr; | 138 HANDLE event_ = nullptr; |
| 170 MMRESULT timer_id_ = 0; | 139 MMRESULT timer_id_ = 0; |
| 171 std::unique_ptr<QueuedTask> task_; | |
| 172 | 140 |
| 173 RTC_DISALLOW_COPY_AND_ASSIGN(MultimediaTimer); | 141 RTC_DISALLOW_COPY_AND_ASSIGN(MultimediaTimer); |
| 174 }; | 142 }; |
| 175 | 143 |
| 144 } // namespace | |
| 145 | |
| 146 class TaskQueue::ThreadState { | |
| 147 public: | |
| 148 ThreadState() {} | |
| 149 ~ThreadState() {} | |
| 150 | |
| 151 void RunThreadMain(); | |
| 152 | |
| 153 private: | |
| 154 bool ProcessQueuedMessages(); | |
| 155 void RunDueTasks(); | |
| 156 void ScheduleNextTimer(); | |
| 157 void CancelTimers(); | |
| 158 | |
| 159 MultimediaTimer timer_; | |
| 160 std::multiset<DelayedTaskInfo> timer_tasks_; | |
|
the sun
2017/03/07 11:24:24
It's not clear to me that multiset is the best str
tommi
2017/03/09 20:27:42
Done.
| |
| 161 UINT_PTR timer_id_ = 0; | |
| 162 }; | |
| 163 | |
| 176 TaskQueue::TaskQueue(const char* queue_name, Priority priority /*= NORMAL*/) | 164 TaskQueue::TaskQueue(const char* queue_name, Priority priority /*= NORMAL*/) |
| 177 : thread_(&TaskQueue::ThreadMain, | 165 : thread_(&TaskQueue::ThreadMain, |
| 178 this, | 166 this, |
| 179 queue_name, | 167 queue_name, |
| 180 TaskQueuePriorityToThreadPriority(priority)) { | 168 TaskQueuePriorityToThreadPriority(priority)) { |
| 181 RTC_DCHECK(queue_name); | 169 RTC_DCHECK(queue_name); |
| 182 thread_.Start(); | 170 thread_.Start(); |
| 183 Event event(false, false); | 171 Event event(false, false); |
| 184 ThreadStartupData startup = {&event, this}; | 172 ThreadStartupData startup = {&event, this}; |
| 185 RTC_CHECK(thread_.QueueAPC(&InitializeQueueThread, | 173 RTC_CHECK(thread_.QueueAPC(&InitializeQueueThread, |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 213 | 201 |
| 214 void TaskQueue::PostTask(std::unique_ptr<QueuedTask> task) { | 202 void TaskQueue::PostTask(std::unique_ptr<QueuedTask> task) { |
| 215 if (::PostThreadMessage(thread_.GetThreadRef(), WM_RUN_TASK, 0, | 203 if (::PostThreadMessage(thread_.GetThreadRef(), WM_RUN_TASK, 0, |
| 216 reinterpret_cast<LPARAM>(task.get()))) { | 204 reinterpret_cast<LPARAM>(task.get()))) { |
| 217 task.release(); | 205 task.release(); |
| 218 } | 206 } |
| 219 } | 207 } |
| 220 | 208 |
| 221 void TaskQueue::PostDelayedTask(std::unique_ptr<QueuedTask> task, | 209 void TaskQueue::PostDelayedTask(std::unique_ptr<QueuedTask> task, |
| 222 uint32_t milliseconds) { | 210 uint32_t milliseconds) { |
| 223 WPARAM wparam; | 211 if (!milliseconds) { |
| 224 #if defined(_WIN64) | 212 PostTask(std::move(task)); |
| 225 // GetTickCount() returns a fairly coarse tick count (resolution or about 8ms) | 213 return; |
| 226 // so this compensation isn't that accurate, but since we have unused 32 bits | 214 } |
| 227 // on Win64, we might as well use them. | 215 |
| 228 wparam = (static_cast<WPARAM>(GetTick()) << 32) | milliseconds; | 216 auto* task_info = new DelayedTaskInfo(milliseconds, std::move(task)); |
|
the sun
2017/03/07 11:24:24
Add a TODO that we should remove this heap alloc.
tommi
2017/03/09 20:27:42
Done.
| |
| 229 #else | 217 if (!::PostThreadMessage(thread_.GetThreadRef(), WM_QUEUE_DELAYED_TASK, 0, |
| 230 wparam = milliseconds; | 218 reinterpret_cast<LPARAM>(task_info))) { |
| 231 #endif | 219 delete task_info; |
| 232 if (::PostThreadMessage(thread_.GetThreadRef(), WM_QUEUE_DELAYED_TASK, wparam, | |
| 233 reinterpret_cast<LPARAM>(task.get()))) { | |
| 234 task.release(); | |
| 235 } | 220 } |
| 236 } | 221 } |
| 237 | 222 |
| 238 void TaskQueue::PostTaskAndReply(std::unique_ptr<QueuedTask> task, | 223 void TaskQueue::PostTaskAndReply(std::unique_ptr<QueuedTask> task, |
| 239 std::unique_ptr<QueuedTask> reply, | 224 std::unique_ptr<QueuedTask> reply, |
| 240 TaskQueue* reply_queue) { | 225 TaskQueue* reply_queue) { |
| 241 QueuedTask* task_ptr = task.release(); | 226 QueuedTask* task_ptr = task.release(); |
| 242 QueuedTask* reply_task_ptr = reply.release(); | 227 QueuedTask* reply_task_ptr = reply.release(); |
| 243 DWORD reply_thread_id = reply_queue->thread_.GetThreadRef(); | 228 DWORD reply_thread_id = reply_queue->thread_.GetThreadRef(); |
| 244 PostTask([task_ptr, reply_task_ptr, reply_thread_id]() { | 229 PostTask([task_ptr, reply_task_ptr, reply_thread_id]() { |
| 245 if (task_ptr->Run()) | 230 if (task_ptr->Run()) |
| 246 delete task_ptr; | 231 delete task_ptr; |
| 247 // If the thread's message queue is full, we can't queue the task and will | 232 // If the thread's message queue is full, we can't queue the task and will |
| 248 // have to drop it (i.e. delete). | 233 // have to drop it (i.e. delete). |
| 249 if (!::PostThreadMessage(reply_thread_id, WM_RUN_TASK, 0, | 234 if (!::PostThreadMessage(reply_thread_id, WM_RUN_TASK, 0, |
| 250 reinterpret_cast<LPARAM>(reply_task_ptr))) { | 235 reinterpret_cast<LPARAM>(reply_task_ptr))) { |
| 251 delete reply_task_ptr; | 236 delete reply_task_ptr; |
| 252 } | 237 } |
| 253 }); | 238 }); |
| 254 } | 239 } |
| 255 | 240 |
| 256 void TaskQueue::PostTaskAndReply(std::unique_ptr<QueuedTask> task, | 241 void TaskQueue::PostTaskAndReply(std::unique_ptr<QueuedTask> task, |
| 257 std::unique_ptr<QueuedTask> reply) { | 242 std::unique_ptr<QueuedTask> reply) { |
| 258 return PostTaskAndReply(std::move(task), std::move(reply), Current()); | 243 return PostTaskAndReply(std::move(task), std::move(reply), Current()); |
| 259 } | 244 } |
| 260 | 245 |
| 261 // static | 246 // static |
| 262 void TaskQueue::ThreadMain(void* context) { | 247 void TaskQueue::ThreadMain(void* context) { |
| 263 HANDLE timer_handles[MultimediaTimer::kMaxTimers]; | 248 ThreadState state; |
| 264 // Active multimedia timers. | 249 state.RunThreadMain(); |
| 265 std::vector<MultimediaTimer> mm_timers; | 250 } |
| 266 // Tasks that have been queued by using SetTimer/WM_TIMER. | |
| 267 DelayedTasks delayed_tasks; | |
| 268 | 251 |
| 252 void TaskQueue::ThreadState::RunThreadMain() { | |
| 269 while (true) { | 253 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 | 254 // 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 | 255 // (e.g. required for InitializeQueueThread and stopping the thread in |
| 279 // PlatformThread). | 256 // PlatformThread). |
| 280 DWORD result = ::MsgWaitForMultipleObjectsEx(count, timer_handles, INFINITE, | 257 DWORD result = ::MsgWaitForMultipleObjectsEx( |
| 281 QS_ALLEVENTS, MWMO_ALERTABLE); | 258 1, timer_.event_for_wait(), INFINITE, QS_ALLEVENTS, MWMO_ALERTABLE); |
| 282 RTC_CHECK_NE(WAIT_FAILED, result); | 259 RTC_CHECK_NE(WAIT_FAILED, result); |
| 283 // If we're not waiting for any timers, then count will be equal to | 260 if (result == (WAIT_OBJECT_0 + 1)) { |
| 284 // WAIT_OBJECT_0. If we're waiting for timers, then |count| represents | 261 // 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 | 262 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; | 263 break; |
| 292 } else if (result < (WAIT_OBJECT_0 + count)) { | 264 } else if (result == WAIT_OBJECT_0) { |
| 293 mm_timers[result].OnEventSignaled(); | 265 // The multimedia timer was signaled. |
| 294 RTC_DCHECK(!mm_timers[result].is_active()); | 266 timer_.Cancel(); |
| 295 // Reuse timer events by moving inactive timers to the back of the vector. | 267 RTC_DCHECK(!timer_tasks_.empty()); |
| 296 // When new delayed tasks are queued, they'll get reused. | 268 RunDueTasks(); |
| 297 if (mm_timers.size() > 1) { | 269 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 { | 270 } else { |
| 314 RTC_DCHECK_EQ(WAIT_IO_COMPLETION, result); | 271 RTC_DCHECK_EQ(WAIT_IO_COMPLETION, result); |
| 315 } | 272 } |
| 316 } | 273 } |
| 317 } | 274 } |
| 318 | 275 |
| 319 // static | 276 bool TaskQueue::ThreadState::ProcessQueuedMessages() { |
| 320 bool TaskQueue::ProcessQueuedMessages(DelayedTasks* delayed_tasks, | |
| 321 std::vector<MultimediaTimer>* timers) { | |
| 322 MSG msg = {}; | 277 MSG msg = {}; |
| 323 while (::PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE) && | 278 while (::PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE) && |
| 324 msg.message != WM_QUIT) { | 279 msg.message != WM_QUIT) { |
| 325 if (!msg.hwnd) { | 280 if (!msg.hwnd) { |
| 326 switch (msg.message) { | 281 switch (msg.message) { |
| 327 case WM_RUN_TASK: { | 282 case WM_RUN_TASK: { |
| 328 QueuedTask* task = reinterpret_cast<QueuedTask*>(msg.lParam); | 283 QueuedTask* task = reinterpret_cast<QueuedTask*>(msg.lParam); |
| 329 if (task->Run()) | 284 if (task->Run()) |
| 330 delete task; | 285 delete task; |
| 331 break; | 286 break; |
| 332 } | 287 } |
| 333 case WM_QUEUE_DELAYED_TASK: { | 288 case WM_QUEUE_DELAYED_TASK: { |
| 334 std::unique_ptr<QueuedTask> task( | 289 std::unique_ptr<DelayedTaskInfo> info( |
| 335 reinterpret_cast<QueuedTask*>(msg.lParam)); | 290 reinterpret_cast<DelayedTaskInfo*>(msg.lParam)); |
| 336 uint32_t milliseconds = msg.wParam & 0xFFFFFFFF; | 291 auto inserted = timer_tasks_.insert(std::move(*info.get())); |
| 337 #if defined(_WIN64) | 292 // If the new task was not added to the front, we're done since a |
| 338 // Subtract the time it took to queue the timer. | 293 // timer has already been scheduled. |
| 339 const DWORD now = GetTick(); | 294 if (inserted == timer_tasks_.begin()) { |
| 340 DWORD post_time = now - (msg.wParam >> 32); | 295 CancelTimers(); |
| 341 milliseconds = | 296 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 } | 297 } |
| 372 break; | 298 break; |
| 373 } | 299 } |
| 374 case WM_TIMER: { | 300 case WM_TIMER: { |
| 301 RTC_DCHECK_EQ(timer_id_, msg.wParam); | |
| 375 ::KillTimer(nullptr, msg.wParam); | 302 ::KillTimer(nullptr, msg.wParam); |
| 376 auto found = delayed_tasks->find(msg.wParam); | 303 timer_id_ = 0; |
| 377 RTC_DCHECK(found != delayed_tasks->end()); | 304 RunDueTasks(); |
| 378 if (!found->second->Run()) | 305 ScheduleNextTimer(); |
| 379 found->second.release(); | |
| 380 delayed_tasks->erase(found); | |
| 381 break; | 306 break; |
| 382 } | 307 } |
| 383 default: | 308 default: |
| 384 RTC_NOTREACHED(); | 309 RTC_NOTREACHED(); |
| 385 break; | 310 break; |
| 386 } | 311 } |
| 387 } else { | 312 } else { |
| 388 ::TranslateMessage(&msg); | 313 ::TranslateMessage(&msg); |
| 389 ::DispatchMessage(&msg); | 314 ::DispatchMessage(&msg); |
| 390 } | 315 } |
| 391 } | 316 } |
| 392 return msg.message != WM_QUIT; | 317 return msg.message != WM_QUIT; |
| 393 } | 318 } |
| 394 | 319 |
| 320 void TaskQueue::ThreadState::RunDueTasks() { | |
| 321 std::multiset<DelayedTaskInfo>::iterator it = timer_tasks_.begin(); | |
| 322 do { | |
| 323 if ((*it).due_time > GetTick()) | |
| 324 break; | |
| 325 (*it).Run(); | |
| 326 ++it; | |
| 327 } while (it != timer_tasks_.end()); | |
| 328 | |
| 329 timer_tasks_.erase(timer_tasks_.begin(), it); | |
|
the sun
2017/03/07 11:24:24
I assume nothing will be erased from the set if bo
tommi
2017/03/09 20:27:42
Yes - however, the implementation has changed slig
| |
| 330 } | |
| 331 | |
| 332 void TaskQueue::ThreadState::ScheduleNextTimer() { | |
| 333 RTC_DCHECK_EQ(timer_id_, 0); | |
| 334 if (timer_tasks_.empty()) | |
| 335 return; | |
| 336 | |
| 337 uint32_t milliseconds = | |
| 338 static_cast<uint32_t>(timer_tasks_.begin()->due_time - GetTick()); | |
|
the sun
2017/03/07 11:24:24
It seems to me you need to manage the case when we
tommi
2017/03/09 20:27:42
Good catch. Done.
| |
| 339 if (!timer_.StartOneShotTimer(milliseconds)) | |
| 340 timer_id_ = ::SetTimer(nullptr, 0, milliseconds, nullptr); | |
| 341 } | |
| 342 | |
| 343 void TaskQueue::ThreadState::CancelTimers() { | |
| 344 timer_.Cancel(); | |
| 345 if (timer_id_) { | |
| 346 ::KillTimer(nullptr, timer_id_); | |
| 347 timer_id_ = 0; | |
| 348 } | |
| 349 } | |
| 350 | |
| 395 } // namespace rtc | 351 } // namespace rtc |
| OLD | NEW |