Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(229)

Side by Side Diff: webrtc/rtc_base/task_queue_win.cc

Issue 3009133002: Convert windows TaskQueue implementation to pimpl convention. (Closed)
Patch Set: Fix ThreadMain, and add PostTask template. Created 3 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « webrtc/rtc_base/task_queue.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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/rtc_base/task_queue.h" 11 #include "webrtc/rtc_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 <algorithm>
17 #include <queue> 17 #include <queue>
18 18
19 #include "webrtc/rtc_base/arraysize.h" 19 #include "webrtc/rtc_base/arraysize.h"
20 #include "webrtc/rtc_base/checks.h" 20 #include "webrtc/rtc_base/checks.h"
21 #include "webrtc/rtc_base/event.h"
21 #include "webrtc/rtc_base/logging.h" 22 #include "webrtc/rtc_base/logging.h"
23 #include "webrtc/rtc_base/platform_thread.h"
24 #include "webrtc/rtc_base/refcount.h"
25 #include "webrtc/rtc_base/refcountedobject.h"
22 #include "webrtc/rtc_base/safe_conversions.h" 26 #include "webrtc/rtc_base/safe_conversions.h"
23 #include "webrtc/rtc_base/timeutils.h" 27 #include "webrtc/rtc_base/timeutils.h"
24 28
25 namespace rtc { 29 namespace rtc {
26 namespace { 30 namespace {
27 #define WM_RUN_TASK WM_USER + 1 31 #define WM_RUN_TASK WM_USER + 1
28 #define WM_QUEUE_DELAYED_TASK WM_USER + 2 32 #define WM_QUEUE_DELAYED_TASK WM_USER + 2
29 33
30 using Priority = TaskQueue::Priority; 34 using Priority = TaskQueue::Priority;
31 35
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after
147 151
148 private: 152 private:
149 HANDLE event_ = nullptr; 153 HANDLE event_ = nullptr;
150 MMRESULT timer_id_ = 0; 154 MMRESULT timer_id_ = 0;
151 155
152 RTC_DISALLOW_COPY_AND_ASSIGN(MultimediaTimer); 156 RTC_DISALLOW_COPY_AND_ASSIGN(MultimediaTimer);
153 }; 157 };
154 158
155 } // namespace 159 } // namespace
156 160
157 class TaskQueue::ThreadState { 161 class TaskQueue::Impl : public RefCountInterface {
158 public: 162 public:
159 explicit ThreadState(HANDLE in_queue) : in_queue_(in_queue) {} 163 Impl(const char* queue_name, TaskQueue* queue, Priority priority);
160 ~ThreadState() {} 164 ~Impl() override;
161 165
162 void RunThreadMain(); 166 static TaskQueue::Impl* Current();
167 static TaskQueue* CurrentQueue();
168
169 // Used for DCHECKing the current queue.
170 bool IsCurrent() const;
171
172 template <class Closure,
173 typename std::enable_if<
174 std::is_copy_constructible<Closure>::value>::type* = nullptr>
175 void PostTask(const Closure& closure) {
perkj_webrtc 2017/09/05 13:30:18 This method is not used and not needed.
nisse-webrtc 2017/09/05 14:39:33 It's used in TaskQueue::Impl::PostTaskAndReply, fo
perkj_webrtc 2017/09/05 15:14:04 ok
176 PostTask(std::unique_ptr<QueuedTask>(new ClosureTask<Closure>(closure)));
177 }
178
179 void PostTask(std::unique_ptr<QueuedTask> task);
180 void PostTaskAndReply(std::unique_ptr<QueuedTask> task,
181 std::unique_ptr<QueuedTask> reply,
182 TaskQueue::Impl* reply_queue);
183
184 void PostDelayedTask(std::unique_ptr<QueuedTask> task, uint32_t milliseconds);
185
186 void RunPendingTasks();
perkj_webrtc 2017/09/05 13:30:18 Everything from here can be made private.
nisse-webrtc 2017/09/05 14:39:33 Done.
187 static void ThreadMain(void* context);
188
189 class WorkerThread : public PlatformThread {
190 public:
191 WorkerThread(ThreadRunFunction func,
192 void* obj,
193 const char* thread_name,
194 ThreadPriority priority)
195 : PlatformThread(func, obj, thread_name, priority) {}
196
197 bool QueueAPC(PAPCFUNC apc_function, ULONG_PTR data) {
198 return PlatformThread::QueueAPC(apc_function, data);
199 }
200 };
201
202 class ThreadState {
203 public:
204 explicit ThreadState(HANDLE in_queue) : in_queue_(in_queue) {}
205 ~ThreadState() {}
206
207 void RunThreadMain();
208
209 private:
210 bool ProcessQueuedMessages();
211 void RunDueTasks();
212 void ScheduleNextTimer();
213 void CancelTimers();
214
215 // Since priority_queue<> by defult orders items in terms of
216 // largest->smallest, using std::less<>, and we want smallest->largest,
217 // we would like to use std::greater<> here. Alas it's only available in
218 // C++14 and later, so we roll our own compare template that that relies on
219 // operator<().
220 template <typename T>
221 struct greater {
222 bool operator()(const T& l, const T& r) { return l > r; }
223 };
224
225 MultimediaTimer timer_;
226 std::priority_queue<DelayedTaskInfo,
227 std::vector<DelayedTaskInfo>,
228 greater<DelayedTaskInfo>>
229 timer_tasks_;
230 UINT_PTR timer_id_ = 0;
231 HANDLE in_queue_;
232 };
163 233
164 private: 234 private:
165 bool ProcessQueuedMessages(); 235 TaskQueue* const queue_;
166 void RunDueTasks(); 236 WorkerThread thread_;
167 void ScheduleNextTimer(); 237 rtc::CriticalSection pending_lock_;
168 void CancelTimers(); 238 std::queue<std::unique_ptr<QueuedTask>> pending_ GUARDED_BY(pending_lock_);
169
170 // Since priority_queue<> by defult orders items in terms of
171 // largest->smallest, using std::less<>, and we want smallest->largest,
172 // we would like to use std::greater<> here. Alas it's only available in
173 // C++14 and later, so we roll our own compare template that that relies on
174 // operator<().
175 template <typename T>
176 struct greater {
177 bool operator()(const T& l, const T& r) { return l > r; }
178 };
179
180 MultimediaTimer timer_;
181 std::priority_queue<DelayedTaskInfo,
182 std::vector<DelayedTaskInfo>,
183 greater<DelayedTaskInfo>>
184 timer_tasks_;
185 UINT_PTR timer_id_ = 0;
186 HANDLE in_queue_; 239 HANDLE in_queue_;
187 }; 240 };
188 241
189 TaskQueue::TaskQueue(const char* queue_name, Priority priority /*= NORMAL*/) 242 TaskQueue::Impl::Impl(const char* queue_name,
190 : thread_(&TaskQueue::ThreadMain, 243 TaskQueue* queue,
244 Priority priority)
245 : queue_(queue),
246 thread_(&TaskQueue::Impl::ThreadMain,
191 this, 247 this,
192 queue_name, 248 queue_name,
193 TaskQueuePriorityToThreadPriority(priority)), 249 TaskQueuePriorityToThreadPriority(priority)),
194 in_queue_(::CreateEvent(nullptr, true, false, nullptr)) { 250 in_queue_(::CreateEvent(nullptr, true, false, nullptr)) {
195 RTC_DCHECK(queue_name); 251 RTC_DCHECK(queue_name);
196 RTC_DCHECK(in_queue_); 252 RTC_DCHECK(in_queue_);
197 thread_.Start(); 253 thread_.Start();
198 Event event(false, false); 254 Event event(false, false);
199 ThreadStartupData startup = {&event, this}; 255 ThreadStartupData startup = {&event, this};
200 RTC_CHECK(thread_.QueueAPC(&InitializeQueueThread, 256 RTC_CHECK(thread_.QueueAPC(&InitializeQueueThread,
201 reinterpret_cast<ULONG_PTR>(&startup))); 257 reinterpret_cast<ULONG_PTR>(&startup)));
202 event.Wait(Event::kForever); 258 event.Wait(Event::kForever);
203 } 259 }
204 260
205 TaskQueue::~TaskQueue() { 261 TaskQueue::Impl::~Impl() {
206 RTC_DCHECK(!IsCurrent()); 262 RTC_DCHECK(!IsCurrent());
207 while (!::PostThreadMessage(thread_.GetThreadRef(), WM_QUIT, 0, 0)) { 263 while (!::PostThreadMessage(thread_.GetThreadRef(), WM_QUIT, 0, 0)) {
208 RTC_CHECK_EQ(ERROR_NOT_ENOUGH_QUOTA, ::GetLastError()); 264 RTC_CHECK_EQ(ERROR_NOT_ENOUGH_QUOTA, ::GetLastError());
209 Sleep(1); 265 Sleep(1);
210 } 266 }
211 thread_.Stop(); 267 thread_.Stop();
212 ::CloseHandle(in_queue_); 268 ::CloseHandle(in_queue_);
213 } 269 }
214 270
215 // static 271 // static
216 TaskQueue* TaskQueue::Current() { 272 TaskQueue::Impl* TaskQueue::Impl::Current() {
217 return static_cast<TaskQueue*>(::TlsGetValue(GetQueuePtrTls())); 273 return static_cast<TaskQueue::Impl*>(::TlsGetValue(GetQueuePtrTls()));
218 } 274 }
219 275
220 bool TaskQueue::IsCurrent() const { 276 // static
277 TaskQueue* TaskQueue::Impl::CurrentQueue() {
278 TaskQueue::Impl* current = Current();
279 return current ? current->queue_ : nullptr;
280 }
281
282 bool TaskQueue::Impl::IsCurrent() const {
221 return IsThreadRefEqual(thread_.GetThreadRef(), CurrentThreadRef()); 283 return IsThreadRefEqual(thread_.GetThreadRef(), CurrentThreadRef());
222 } 284 }
223 285
224 void TaskQueue::PostTask(std::unique_ptr<QueuedTask> task) { 286 void TaskQueue::Impl::PostTask(std::unique_ptr<QueuedTask> task) {
225 rtc::CritScope lock(&pending_lock_); 287 rtc::CritScope lock(&pending_lock_);
226 pending_.push(std::move(task)); 288 pending_.push(std::move(task));
227 ::SetEvent(in_queue_); 289 ::SetEvent(in_queue_);
228 } 290 }
229 291
230 void TaskQueue::PostDelayedTask(std::unique_ptr<QueuedTask> task, 292 void TaskQueue::Impl::PostDelayedTask(std::unique_ptr<QueuedTask> task,
231 uint32_t milliseconds) { 293 uint32_t milliseconds) {
232 if (!milliseconds) { 294 if (!milliseconds) {
233 PostTask(std::move(task)); 295 PostTask(std::move(task));
234 return; 296 return;
235 } 297 }
236 298
237 // TODO(tommi): Avoid this allocation. It is currently here since 299 // TODO(tommi): Avoid this allocation. It is currently here since
238 // the timestamp stored in the task info object, is a 64bit timestamp 300 // the timestamp stored in the task info object, is a 64bit timestamp
239 // and WPARAM is 32bits in 32bit builds. Otherwise, we could pass the 301 // and WPARAM is 32bits in 32bit builds. Otherwise, we could pass the
240 // task pointer and timestamp as LPARAM and WPARAM. 302 // task pointer and timestamp as LPARAM and WPARAM.
241 auto* task_info = new DelayedTaskInfo(milliseconds, std::move(task)); 303 auto* task_info = new DelayedTaskInfo(milliseconds, std::move(task));
242 if (!::PostThreadMessage(thread_.GetThreadRef(), WM_QUEUE_DELAYED_TASK, 0, 304 if (!::PostThreadMessage(thread_.GetThreadRef(), WM_QUEUE_DELAYED_TASK, 0,
243 reinterpret_cast<LPARAM>(task_info))) { 305 reinterpret_cast<LPARAM>(task_info))) {
244 delete task_info; 306 delete task_info;
245 } 307 }
246 } 308 }
247 309
248 void TaskQueue::PostTaskAndReply(std::unique_ptr<QueuedTask> task, 310 void TaskQueue::Impl::PostTaskAndReply(std::unique_ptr<QueuedTask> task,
249 std::unique_ptr<QueuedTask> reply, 311 std::unique_ptr<QueuedTask> reply,
250 TaskQueue* reply_queue) { 312 TaskQueue::Impl* reply_queue) {
251 QueuedTask* task_ptr = task.release(); 313 QueuedTask* task_ptr = task.release();
252 QueuedTask* reply_task_ptr = reply.release(); 314 QueuedTask* reply_task_ptr = reply.release();
253 DWORD reply_thread_id = reply_queue->thread_.GetThreadRef(); 315 DWORD reply_thread_id = reply_queue->thread_.GetThreadRef();
254 PostTask([task_ptr, reply_task_ptr, reply_thread_id]() { 316 PostTask([task_ptr, reply_task_ptr, reply_thread_id]() {
255 if (task_ptr->Run()) 317 if (task_ptr->Run())
256 delete task_ptr; 318 delete task_ptr;
257 // If the thread's message queue is full, we can't queue the task and will 319 // If the thread's message queue is full, we can't queue the task and will
258 // have to drop it (i.e. delete). 320 // have to drop it (i.e. delete).
259 if (!::PostThreadMessage(reply_thread_id, WM_RUN_TASK, 0, 321 if (!::PostThreadMessage(reply_thread_id, WM_RUN_TASK, 0,
260 reinterpret_cast<LPARAM>(reply_task_ptr))) { 322 reinterpret_cast<LPARAM>(reply_task_ptr))) {
261 delete reply_task_ptr; 323 delete reply_task_ptr;
262 } 324 }
263 }); 325 });
264 } 326 }
265 327
266 void TaskQueue::PostTaskAndReply(std::unique_ptr<QueuedTask> task, 328 void TaskQueue::Impl::RunPendingTasks() {
267 std::unique_ptr<QueuedTask> reply) {
268 return PostTaskAndReply(std::move(task), std::move(reply), Current());
269 }
270
271 void TaskQueue::RunPendingTasks() {
272 while (true) { 329 while (true) {
273 std::unique_ptr<QueuedTask> task; 330 std::unique_ptr<QueuedTask> task;
274 { 331 {
275 rtc::CritScope lock(&pending_lock_); 332 rtc::CritScope lock(&pending_lock_);
276 if (pending_.empty()) 333 if (pending_.empty())
277 break; 334 break;
278 task = std::move(pending_.front()); 335 task = std::move(pending_.front());
279 pending_.pop(); 336 pending_.pop();
280 } 337 }
281 338
282 if (!task->Run()) 339 if (!task->Run())
283 task.release(); 340 task.release();
284 } 341 }
285 } 342 }
286 343
287 // static 344 // static
288 void TaskQueue::ThreadMain(void* context) { 345 void TaskQueue::Impl::ThreadMain(void* context) {
289 ThreadState state(static_cast<TaskQueue*>(context)->in_queue_); 346 ThreadState state(static_cast<TaskQueue::Impl*>(context)->in_queue_);
290 state.RunThreadMain(); 347 state.RunThreadMain();
291 } 348 }
292 349
293 void TaskQueue::ThreadState::RunThreadMain() { 350 void TaskQueue::Impl::ThreadState::RunThreadMain() {
294 HANDLE handles[2] = { *timer_.event_for_wait(), in_queue_ }; 351 HANDLE handles[2] = { *timer_.event_for_wait(), in_queue_ };
295 while (true) { 352 while (true) {
296 // Make sure we do an alertable wait as that's required to allow APCs to run 353 // Make sure we do an alertable wait as that's required to allow APCs to run
297 // (e.g. required for InitializeQueueThread and stopping the thread in 354 // (e.g. required for InitializeQueueThread and stopping the thread in
298 // PlatformThread). 355 // PlatformThread).
299 DWORD result = ::MsgWaitForMultipleObjectsEx( 356 DWORD result = ::MsgWaitForMultipleObjectsEx(
300 arraysize(handles), handles, INFINITE, QS_ALLEVENTS, MWMO_ALERTABLE); 357 arraysize(handles), handles, INFINITE, QS_ALLEVENTS, MWMO_ALERTABLE);
301 RTC_CHECK_NE(WAIT_FAILED, result); 358 RTC_CHECK_NE(WAIT_FAILED, result);
302 if (result == (WAIT_OBJECT_0 + 2)) { 359 if (result == (WAIT_OBJECT_0 + 2)) {
303 // There are messages in the message queue that need to be handled. 360 // There are messages in the message queue that need to be handled.
304 if (!ProcessQueuedMessages()) 361 if (!ProcessQueuedMessages())
305 break; 362 break;
306 } 363 }
307 364
308 if (result == WAIT_OBJECT_0 || (!timer_tasks_.empty() && 365 if (result == WAIT_OBJECT_0 || (!timer_tasks_.empty() &&
309 ::WaitForSingleObject(*timer_.event_for_wait(), 0) == WAIT_OBJECT_0)) { 366 ::WaitForSingleObject(*timer_.event_for_wait(), 0) == WAIT_OBJECT_0)) {
310 // The multimedia timer was signaled. 367 // The multimedia timer was signaled.
311 timer_.Cancel(); 368 timer_.Cancel();
312 RunDueTasks(); 369 RunDueTasks();
313 ScheduleNextTimer(); 370 ScheduleNextTimer();
314 } 371 }
315 372
316 if (result == (WAIT_OBJECT_0 + 1)) { 373 if (result == (WAIT_OBJECT_0 + 1)) {
317 ::ResetEvent(in_queue_); 374 ::ResetEvent(in_queue_);
318 TaskQueue::Current()->RunPendingTasks(); 375 TaskQueue::Impl::Current()->RunPendingTasks();
319 } 376 }
320 } 377 }
321 } 378 }
322 379
323 bool TaskQueue::ThreadState::ProcessQueuedMessages() { 380 bool TaskQueue::Impl::ThreadState::ProcessQueuedMessages() {
324 MSG msg = {}; 381 MSG msg = {};
325 // To protect against overly busy message queues, we limit the time 382 // To protect against overly busy message queues, we limit the time
326 // we process tasks to a few milliseconds. If we don't do that, there's 383 // we process tasks to a few milliseconds. If we don't do that, there's
327 // a chance that timer tasks won't ever run. 384 // a chance that timer tasks won't ever run.
328 static const int kMaxTaskProcessingTimeMs = 500; 385 static const int kMaxTaskProcessingTimeMs = 500;
329 auto start = GetTick(); 386 auto start = GetTick();
330 while (::PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE) && 387 while (::PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE) &&
331 msg.message != WM_QUIT) { 388 msg.message != WM_QUIT) {
332 if (!msg.hwnd) { 389 if (!msg.hwnd) {
333 switch (msg.message) { 390 switch (msg.message) {
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
367 ::TranslateMessage(&msg); 424 ::TranslateMessage(&msg);
368 ::DispatchMessage(&msg); 425 ::DispatchMessage(&msg);
369 } 426 }
370 427
371 if (GetTick() > start + kMaxTaskProcessingTimeMs) 428 if (GetTick() > start + kMaxTaskProcessingTimeMs)
372 break; 429 break;
373 } 430 }
374 return msg.message != WM_QUIT; 431 return msg.message != WM_QUIT;
375 } 432 }
376 433
377 void TaskQueue::ThreadState::RunDueTasks() { 434 void TaskQueue::Impl::ThreadState::RunDueTasks() {
378 RTC_DCHECK(!timer_tasks_.empty()); 435 RTC_DCHECK(!timer_tasks_.empty());
379 auto now = GetTick(); 436 auto now = GetTick();
380 do { 437 do {
381 const auto& top = timer_tasks_.top(); 438 const auto& top = timer_tasks_.top();
382 if (top.due_time() > now) 439 if (top.due_time() > now)
383 break; 440 break;
384 top.Run(); 441 top.Run();
385 timer_tasks_.pop(); 442 timer_tasks_.pop();
386 } while (!timer_tasks_.empty()); 443 } while (!timer_tasks_.empty());
387 } 444 }
388 445
389 void TaskQueue::ThreadState::ScheduleNextTimer() { 446 void TaskQueue::Impl::ThreadState::ScheduleNextTimer() {
390 RTC_DCHECK_EQ(timer_id_, 0); 447 RTC_DCHECK_EQ(timer_id_, 0);
391 if (timer_tasks_.empty()) 448 if (timer_tasks_.empty())
392 return; 449 return;
393 450
394 const auto& next_task = timer_tasks_.top(); 451 const auto& next_task = timer_tasks_.top();
395 int64_t delay_ms = std::max(0ll, next_task.due_time() - GetTick()); 452 int64_t delay_ms = std::max(0ll, next_task.due_time() - GetTick());
396 uint32_t milliseconds = rtc::dchecked_cast<uint32_t>(delay_ms); 453 uint32_t milliseconds = rtc::dchecked_cast<uint32_t>(delay_ms);
397 if (!timer_.StartOneShotTimer(milliseconds)) 454 if (!timer_.StartOneShotTimer(milliseconds))
398 timer_id_ = ::SetTimer(nullptr, 0, milliseconds, nullptr); 455 timer_id_ = ::SetTimer(nullptr, 0, milliseconds, nullptr);
399 } 456 }
400 457
401 void TaskQueue::ThreadState::CancelTimers() { 458 void TaskQueue::Impl::ThreadState::CancelTimers() {
402 timer_.Cancel(); 459 timer_.Cancel();
403 if (timer_id_) { 460 if (timer_id_) {
404 ::KillTimer(nullptr, timer_id_); 461 ::KillTimer(nullptr, timer_id_);
405 timer_id_ = 0; 462 timer_id_ = 0;
406 } 463 }
407 } 464 }
408 465
466 // Boilerplate for the PIMPL pattern.
467 TaskQueue::TaskQueue(const char* queue_name, Priority priority)
468 : impl_(new RefCountedObject<TaskQueue::Impl>(queue_name, this, priority)) {
469 }
470
471 TaskQueue::~TaskQueue() {}
472
473 // static
474 TaskQueue* TaskQueue::Current() {
475 return TaskQueue::Impl::CurrentQueue();
476 }
477
478 // Used for DCHECKing the current queue.
479 bool TaskQueue::IsCurrent() const {
480 return impl_->IsCurrent();
481 }
482
483 void TaskQueue::PostTask(std::unique_ptr<QueuedTask> task) {
484 return TaskQueue::impl_->PostTask(std::move(task));
485 }
486
487 void TaskQueue::PostTaskAndReply(std::unique_ptr<QueuedTask> task,
488 std::unique_ptr<QueuedTask> reply,
489 TaskQueue* reply_queue) {
490 return TaskQueue::impl_->PostTaskAndReply(std::move(task), std::move(reply),
491 reply_queue->impl_.get());
492 }
493
494 void TaskQueue::PostTaskAndReply(std::unique_ptr<QueuedTask> task,
495 std::unique_ptr<QueuedTask> reply) {
496 return TaskQueue::impl_->PostTaskAndReply(std::move(task), std::move(reply),
497 impl_.get());
498 }
499
500 void TaskQueue::PostDelayedTask(std::unique_ptr<QueuedTask> task,
501 uint32_t milliseconds) {
502 return TaskQueue::impl_->PostDelayedTask(std::move(task), milliseconds);
503 }
504
409 } // namespace rtc 505 } // namespace rtc
OLDNEW
« no previous file with comments | « webrtc/rtc_base/task_queue.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698