blob: d3b1a7c5cb157407997c6358fad0c9a7f678bf90 [file] [log] [blame]
tommic06b1332016-05-14 11:31:40 -07001/*
2 * Copyright 2016 The WebRTC Project Authors. All rights reserved.
3 *
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
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "rtc_base/task_queue.h"
tommic06b1332016-05-14 11:31:40 -070012
13#include <fcntl.h>
tommi8c80c6e2017-02-23 00:34:52 -080014#include <signal.h>
tommic06b1332016-05-14 11:31:40 -070015#include <string.h>
16#include <unistd.h>
Danil Chapovalov02fddf62018-02-12 12:41:16 +010017#include <list>
tommic06b1332016-05-14 11:31:40 -070018
19#include "base/third_party/libevent/event.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020020#include "rtc_base/checks.h"
Danil Chapovalov02fddf62018-02-12 12:41:16 +010021#include "rtc_base/criticalsection.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020022#include "rtc_base/logging.h"
Karl Wiberge40468b2017-11-22 10:42:26 +010023#include "rtc_base/numerics/safe_conversions.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020024#include "rtc_base/platform_thread.h"
25#include "rtc_base/refcount.h"
26#include "rtc_base/refcountedobject.h"
Niels Möllera12c42a2018-07-25 16:05:48 +020027#include "rtc_base/system/unused.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020028#include "rtc_base/task_queue.h"
29#include "rtc_base/task_queue_posix.h"
30#include "rtc_base/timeutils.h"
tommic06b1332016-05-14 11:31:40 -070031
32namespace rtc {
33using internal::GetQueuePtrTls;
34using internal::AutoSetCurrentQueuePtr;
35
36namespace {
37static const char kQuit = 1;
38static const char kRunTask = 2;
tommi8c80c6e2017-02-23 00:34:52 -080039static const char kRunReplyTask = 3;
40
tommic9bb7912017-02-24 10:42:14 -080041using Priority = TaskQueue::Priority;
42
tommi8c80c6e2017-02-23 00:34:52 -080043// This ignores the SIGPIPE signal on the calling thread.
44// This signal can be fired when trying to write() to a pipe that's being
45// closed or while closing a pipe that's being written to.
46// We can run into that situation (e.g. reply tasks that don't get a chance to
47// run because the task queue is being deleted) so we ignore this signal and
48// continue as normal.
49// As a side note for this implementation, it would be great if we could safely
50// restore the sigmask, but unfortunately the operation of restoring it, can
51// itself actually cause SIGPIPE to be signaled :-| (e.g. on MacOS)
52// The SIGPIPE signal by default causes the process to be terminated, so we
53// don't want to risk that.
54// An alternative to this approach is to ignore the signal for the whole
55// process:
56// signal(SIGPIPE, SIG_IGN);
57void IgnoreSigPipeSignalOnCurrentThread() {
58 sigset_t sigpipe_mask;
59 sigemptyset(&sigpipe_mask);
60 sigaddset(&sigpipe_mask, SIGPIPE);
61 pthread_sigmask(SIG_BLOCK, &sigpipe_mask, nullptr);
62}
tommic06b1332016-05-14 11:31:40 -070063
64struct TimerEvent {
65 explicit TimerEvent(std::unique_ptr<QueuedTask> task)
66 : task(std::move(task)) {}
67 ~TimerEvent() { event_del(&ev); }
68 event ev;
69 std::unique_ptr<QueuedTask> task;
70};
71
72bool SetNonBlocking(int fd) {
73 const int flags = fcntl(fd, F_GETFL);
74 RTC_CHECK(flags != -1);
75 return (flags & O_NONBLOCK) || fcntl(fd, F_SETFL, flags | O_NONBLOCK) != -1;
76}
tommi1666b612016-07-13 10:58:12 -070077
78// TODO(tommi): This is a hack to support two versions of libevent that we're
79// compatible with. The method we really want to call is event_assign(),
80// since event_set() has been marked as deprecated (and doesn't accept
81// passing event_base__ as a parameter). However, the version of libevent
82// that we have in Chromium, doesn't have event_assign(), so we need to call
83// event_set() there.
84void EventAssign(struct event* ev,
85 struct event_base* base,
86 int fd,
87 short events,
88 void (*callback)(int, short, void*),
89 void* arg) {
90#if defined(_EVENT2_EVENT_H_)
91 RTC_CHECK_EQ(0, event_assign(ev, base, fd, events, callback, arg));
92#else
93 event_set(ev, fd, events, callback, arg);
94 RTC_CHECK_EQ(0, event_base_set(base, ev));
95#endif
96}
tommic9bb7912017-02-24 10:42:14 -080097
98ThreadPriority TaskQueuePriorityToThreadPriority(Priority priority) {
99 switch (priority) {
100 case Priority::HIGH:
101 return kRealtimePriority;
102 case Priority::LOW:
103 return kLowPriority;
104 case Priority::NORMAL:
105 return kNormalPriority;
106 default:
107 RTC_NOTREACHED();
108 break;
109 }
110 return kNormalPriority;
111}
tommic06b1332016-05-14 11:31:40 -0700112} // namespace
113
perkj650fdae2017-08-25 05:00:11 -0700114class TaskQueue::Impl : public RefCountInterface {
115 public:
116 explicit Impl(const char* queue_name,
117 TaskQueue* queue,
118 Priority priority = Priority::NORMAL);
119 ~Impl() override;
120
121 static TaskQueue::Impl* Current();
122 static TaskQueue* CurrentQueue();
123
124 // Used for DCHECKing the current queue.
perkj650fdae2017-08-25 05:00:11 -0700125 bool IsCurrent() const;
126
127 void PostTask(std::unique_ptr<QueuedTask> task);
128 void PostTaskAndReply(std::unique_ptr<QueuedTask> task,
129 std::unique_ptr<QueuedTask> reply,
130 TaskQueue::Impl* reply_queue);
131
132 void PostDelayedTask(std::unique_ptr<QueuedTask> task, uint32_t milliseconds);
133
134 private:
135 static void ThreadMain(void* context);
136 static void OnWakeup(int socket, short flags, void* context); // NOLINT
137 static void RunTask(int fd, short flags, void* context); // NOLINT
138 static void RunTimer(int fd, short flags, void* context); // NOLINT
139
140 class ReplyTaskOwner;
141 class PostAndReplyTask;
142 class SetTimerTask;
143
144 typedef RefCountedObject<ReplyTaskOwner> ReplyTaskOwnerRef;
145
146 void PrepareReplyTask(scoped_refptr<ReplyTaskOwnerRef> reply_task);
147
148 struct QueueContext;
149 TaskQueue* const queue_;
150 int wakeup_pipe_in_ = -1;
151 int wakeup_pipe_out_ = -1;
152 event_base* event_base_;
153 std::unique_ptr<event> wakeup_event_;
154 PlatformThread thread_;
155 rtc::CriticalSection pending_lock_;
danilchap3c6abd22017-09-06 05:46:29 -0700156 std::list<std::unique_ptr<QueuedTask>> pending_ RTC_GUARDED_BY(pending_lock_);
perkj650fdae2017-08-25 05:00:11 -0700157 std::list<scoped_refptr<ReplyTaskOwnerRef>> pending_replies_
danilchap3c6abd22017-09-06 05:46:29 -0700158 RTC_GUARDED_BY(pending_lock_);
perkj650fdae2017-08-25 05:00:11 -0700159};
160
161struct TaskQueue::Impl::QueueContext {
162 explicit QueueContext(TaskQueue::Impl* q) : queue(q), is_active(true) {}
163 TaskQueue::Impl* queue;
tommic06b1332016-05-14 11:31:40 -0700164 bool is_active;
165 // Holds a list of events pending timers for cleanup when the loop exits.
166 std::list<TimerEvent*> pending_timers_;
167};
168
tommi8c80c6e2017-02-23 00:34:52 -0800169// Posting a reply task is tricky business. This class owns the reply task
170// and a reference to it is held by both the reply queue and the first task.
171// Here's an outline of what happens when dealing with a reply task.
172// * The ReplyTaskOwner owns the |reply_| task.
173// * One ref owned by PostAndReplyTask
174// * One ref owned by the reply TaskQueue
175// * ReplyTaskOwner has a flag |run_task_| initially set to false.
176// * ReplyTaskOwner has a method: HasOneRef() (provided by RefCountedObject).
177// * After successfully running the original |task_|, PostAndReplyTask() calls
178// set_should_run_task(). This sets |run_task_| to true.
179// * In PostAndReplyTask's dtor:
180// * It releases its reference to ReplyTaskOwner (important to do this first).
181// * Sends (write()) a kRunReplyTask message to the reply queue's pipe.
182// * PostAndReplyTask doesn't care if write() fails, but when it does:
183// * The reply queue is gone.
184// * ReplyTaskOwner has already been deleted and the reply task too.
185// * If write() succeeds:
186// * ReplyQueue receives the kRunReplyTask message
187// * Goes through all pending tasks, finding the first that HasOneRef()
188// * Calls ReplyTaskOwner::Run()
189// * if set_should_run_task() was called, the reply task will be run
190// * Release the reference to ReplyTaskOwner
191// * ReplyTaskOwner and associated |reply_| are deleted.
perkj650fdae2017-08-25 05:00:11 -0700192class TaskQueue::Impl::ReplyTaskOwner {
tommi8c80c6e2017-02-23 00:34:52 -0800193 public:
194 ReplyTaskOwner(std::unique_ptr<QueuedTask> reply)
195 : reply_(std::move(reply)) {}
196
197 void Run() {
198 RTC_DCHECK(reply_);
199 if (run_task_) {
200 if (!reply_->Run())
201 reply_.release();
202 }
203 reply_.reset();
204 }
205
206 void set_should_run_task() {
207 RTC_DCHECK(!run_task_);
208 run_task_ = true;
209 }
210
211 private:
212 std::unique_ptr<QueuedTask> reply_;
213 bool run_task_ = false;
214};
215
perkj650fdae2017-08-25 05:00:11 -0700216class TaskQueue::Impl::PostAndReplyTask : public QueuedTask {
tommic06b1332016-05-14 11:31:40 -0700217 public:
218 PostAndReplyTask(std::unique_ptr<QueuedTask> task,
219 std::unique_ptr<QueuedTask> reply,
perkj650fdae2017-08-25 05:00:11 -0700220 TaskQueue::Impl* reply_queue,
tommi8c80c6e2017-02-23 00:34:52 -0800221 int reply_pipe)
tommic06b1332016-05-14 11:31:40 -0700222 : task_(std::move(task)),
tommi8c80c6e2017-02-23 00:34:52 -0800223 reply_pipe_(reply_pipe),
224 reply_task_owner_(
225 new RefCountedObject<ReplyTaskOwner>(std::move(reply))) {
226 reply_queue->PrepareReplyTask(reply_task_owner_);
tommic06b1332016-05-14 11:31:40 -0700227 }
228
229 ~PostAndReplyTask() override {
tommi8c80c6e2017-02-23 00:34:52 -0800230 reply_task_owner_ = nullptr;
231 IgnoreSigPipeSignalOnCurrentThread();
232 // Send a signal to the reply queue that the reply task can run now.
233 // Depending on whether |set_should_run_task()| was called by the
234 // PostAndReplyTask(), the reply task may or may not actually run.
235 // In either case, it will be deleted.
236 char message = kRunReplyTask;
Tommi79a55602018-02-03 11:17:09 +0100237 RTC_UNUSED(write(reply_pipe_, &message, sizeof(message)));
tommic06b1332016-05-14 11:31:40 -0700238 }
239
240 private:
241 bool Run() override {
242 if (!task_->Run())
243 task_.release();
tommi8c80c6e2017-02-23 00:34:52 -0800244 reply_task_owner_->set_should_run_task();
tommic06b1332016-05-14 11:31:40 -0700245 return true;
246 }
247
tommic06b1332016-05-14 11:31:40 -0700248 std::unique_ptr<QueuedTask> task_;
tommi8c80c6e2017-02-23 00:34:52 -0800249 int reply_pipe_;
250 scoped_refptr<RefCountedObject<ReplyTaskOwner>> reply_task_owner_;
tommic06b1332016-05-14 11:31:40 -0700251};
252
perkj650fdae2017-08-25 05:00:11 -0700253class TaskQueue::Impl::SetTimerTask : public QueuedTask {
tommic06b1332016-05-14 11:31:40 -0700254 public:
255 SetTimerTask(std::unique_ptr<QueuedTask> task, uint32_t milliseconds)
256 : task_(std::move(task)),
257 milliseconds_(milliseconds),
258 posted_(Time32()) {}
259
260 private:
261 bool Run() override {
262 // Compensate for the time that has passed since construction
263 // and until we got here.
264 uint32_t post_time = Time32() - posted_;
perkj650fdae2017-08-25 05:00:11 -0700265 TaskQueue::Impl::Current()->PostDelayedTask(
tommic06b1332016-05-14 11:31:40 -0700266 std::move(task_),
267 post_time > milliseconds_ ? 0 : milliseconds_ - post_time);
268 return true;
269 }
270
271 std::unique_ptr<QueuedTask> task_;
272 const uint32_t milliseconds_;
273 const uint32_t posted_;
274};
275
perkj650fdae2017-08-25 05:00:11 -0700276TaskQueue::Impl::Impl(const char* queue_name,
277 TaskQueue* queue,
278 Priority priority /*= NORMAL*/)
279 : queue_(queue),
280 event_base_(event_base_new()),
tommic06b1332016-05-14 11:31:40 -0700281 wakeup_event_(new event()),
perkj650fdae2017-08-25 05:00:11 -0700282 thread_(&TaskQueue::Impl::ThreadMain,
tommic9bb7912017-02-24 10:42:14 -0800283 this,
284 queue_name,
285 TaskQueuePriorityToThreadPriority(priority)) {
tommic06b1332016-05-14 11:31:40 -0700286 RTC_DCHECK(queue_name);
287 int fds[2];
288 RTC_CHECK(pipe(fds) == 0);
289 SetNonBlocking(fds[0]);
290 SetNonBlocking(fds[1]);
291 wakeup_pipe_out_ = fds[0];
292 wakeup_pipe_in_ = fds[1];
tommi8c80c6e2017-02-23 00:34:52 -0800293
tommi1666b612016-07-13 10:58:12 -0700294 EventAssign(wakeup_event_.get(), event_base_, wakeup_pipe_out_,
295 EV_READ | EV_PERSIST, OnWakeup, this);
tommic06b1332016-05-14 11:31:40 -0700296 event_add(wakeup_event_.get(), 0);
297 thread_.Start();
298}
299
perkj650fdae2017-08-25 05:00:11 -0700300TaskQueue::Impl::~Impl() {
tommic06b1332016-05-14 11:31:40 -0700301 RTC_DCHECK(!IsCurrent());
302 struct timespec ts;
303 char message = kQuit;
304 while (write(wakeup_pipe_in_, &message, sizeof(message)) != sizeof(message)) {
305 // The queue is full, so we have no choice but to wait and retry.
306 RTC_CHECK_EQ(EAGAIN, errno);
307 ts.tv_sec = 0;
308 ts.tv_nsec = 1000000;
309 nanosleep(&ts, nullptr);
310 }
311
312 thread_.Stop();
313
314 event_del(wakeup_event_.get());
tommi8c80c6e2017-02-23 00:34:52 -0800315
316 IgnoreSigPipeSignalOnCurrentThread();
317
tommic06b1332016-05-14 11:31:40 -0700318 close(wakeup_pipe_in_);
319 close(wakeup_pipe_out_);
320 wakeup_pipe_in_ = -1;
321 wakeup_pipe_out_ = -1;
322
tommic06b1332016-05-14 11:31:40 -0700323 event_base_free(event_base_);
324}
325
326// static
perkj650fdae2017-08-25 05:00:11 -0700327TaskQueue::Impl* TaskQueue::Impl::Current() {
tommic06b1332016-05-14 11:31:40 -0700328 QueueContext* ctx =
329 static_cast<QueueContext*>(pthread_getspecific(GetQueuePtrTls()));
330 return ctx ? ctx->queue : nullptr;
331}
332
333// static
perkj650fdae2017-08-25 05:00:11 -0700334TaskQueue* TaskQueue::Impl::CurrentQueue() {
335 TaskQueue::Impl* current = Current();
336 if (current) {
337 return current->queue_;
338 }
339 return nullptr;
340}
341
perkj650fdae2017-08-25 05:00:11 -0700342bool TaskQueue::Impl::IsCurrent() const {
tommic06b1332016-05-14 11:31:40 -0700343 return IsThreadRefEqual(thread_.GetThreadRef(), CurrentThreadRef());
344}
345
perkj650fdae2017-08-25 05:00:11 -0700346void TaskQueue::Impl::PostTask(std::unique_ptr<QueuedTask> task) {
tommic06b1332016-05-14 11:31:40 -0700347 RTC_DCHECK(task.get());
348 // libevent isn't thread safe. This means that we can't use methods such
349 // as event_base_once to post tasks to the worker thread from a different
350 // thread. However, we can use it when posting from the worker thread itself.
351 if (IsCurrent()) {
perkj650fdae2017-08-25 05:00:11 -0700352 if (event_base_once(event_base_, -1, EV_TIMEOUT, &TaskQueue::Impl::RunTask,
tommic06b1332016-05-14 11:31:40 -0700353 task.get(), nullptr) == 0) {
354 task.release();
355 }
356 } else {
357 QueuedTask* task_id = task.get(); // Only used for comparison.
358 {
359 CritScope lock(&pending_lock_);
360 pending_.push_back(std::move(task));
361 }
362 char message = kRunTask;
363 if (write(wakeup_pipe_in_, &message, sizeof(message)) != sizeof(message)) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100364 RTC_LOG(WARNING) << "Failed to queue task.";
tommic06b1332016-05-14 11:31:40 -0700365 CritScope lock(&pending_lock_);
366 pending_.remove_if([task_id](std::unique_ptr<QueuedTask>& t) {
367 return t.get() == task_id;
368 });
369 }
370 }
371}
372
perkj650fdae2017-08-25 05:00:11 -0700373void TaskQueue::Impl::PostDelayedTask(std::unique_ptr<QueuedTask> task,
374 uint32_t milliseconds) {
tommic06b1332016-05-14 11:31:40 -0700375 if (IsCurrent()) {
376 TimerEvent* timer = new TimerEvent(std::move(task));
perkj650fdae2017-08-25 05:00:11 -0700377 EventAssign(&timer->ev, event_base_, -1, 0, &TaskQueue::Impl::RunTimer,
378 timer);
tommic06b1332016-05-14 11:31:40 -0700379 QueueContext* ctx =
380 static_cast<QueueContext*>(pthread_getspecific(GetQueuePtrTls()));
381 ctx->pending_timers_.push_back(timer);
kwiberg5b9746e2017-08-16 04:52:35 -0700382 timeval tv = {rtc::dchecked_cast<int>(milliseconds / 1000),
383 rtc::dchecked_cast<int>(milliseconds % 1000) * 1000};
tommic06b1332016-05-14 11:31:40 -0700384 event_add(&timer->ev, &tv);
385 } else {
386 PostTask(std::unique_ptr<QueuedTask>(
387 new SetTimerTask(std::move(task), milliseconds)));
388 }
389}
390
perkj650fdae2017-08-25 05:00:11 -0700391void TaskQueue::Impl::PostTaskAndReply(std::unique_ptr<QueuedTask> task,
392 std::unique_ptr<QueuedTask> reply,
393 TaskQueue::Impl* reply_queue) {
tommic06b1332016-05-14 11:31:40 -0700394 std::unique_ptr<QueuedTask> wrapper_task(
tommi8c80c6e2017-02-23 00:34:52 -0800395 new PostAndReplyTask(std::move(task), std::move(reply), reply_queue,
396 reply_queue->wakeup_pipe_in_));
tommic06b1332016-05-14 11:31:40 -0700397 PostTask(std::move(wrapper_task));
398}
399
tommic06b1332016-05-14 11:31:40 -0700400// static
perkj650fdae2017-08-25 05:00:11 -0700401void TaskQueue::Impl::ThreadMain(void* context) {
402 TaskQueue::Impl* me = static_cast<TaskQueue::Impl*>(context);
tommic06b1332016-05-14 11:31:40 -0700403
404 QueueContext queue_context(me);
405 pthread_setspecific(GetQueuePtrTls(), &queue_context);
406
407 while (queue_context.is_active)
408 event_base_loop(me->event_base_, 0);
409
410 pthread_setspecific(GetQueuePtrTls(), nullptr);
411
412 for (TimerEvent* timer : queue_context.pending_timers_)
413 delete timer;
tommic06b1332016-05-14 11:31:40 -0700414}
415
416// static
perkj650fdae2017-08-25 05:00:11 -0700417void TaskQueue::Impl::OnWakeup(int socket,
418 short flags,
419 void* context) { // NOLINT
tommic06b1332016-05-14 11:31:40 -0700420 QueueContext* ctx =
421 static_cast<QueueContext*>(pthread_getspecific(GetQueuePtrTls()));
422 RTC_DCHECK(ctx->queue->wakeup_pipe_out_ == socket);
423 char buf;
424 RTC_CHECK(sizeof(buf) == read(socket, &buf, sizeof(buf)));
425 switch (buf) {
426 case kQuit:
427 ctx->is_active = false;
428 event_base_loopbreak(ctx->queue->event_base_);
429 break;
430 case kRunTask: {
431 std::unique_ptr<QueuedTask> task;
432 {
433 CritScope lock(&ctx->queue->pending_lock_);
434 RTC_DCHECK(!ctx->queue->pending_.empty());
435 task = std::move(ctx->queue->pending_.front());
436 ctx->queue->pending_.pop_front();
437 RTC_DCHECK(task.get());
438 }
439 if (!task->Run())
440 task.release();
441 break;
442 }
tommi8c80c6e2017-02-23 00:34:52 -0800443 case kRunReplyTask: {
444 scoped_refptr<ReplyTaskOwnerRef> reply_task;
445 {
446 CritScope lock(&ctx->queue->pending_lock_);
447 for (auto it = ctx->queue->pending_replies_.begin();
448 it != ctx->queue->pending_replies_.end(); ++it) {
449 if ((*it)->HasOneRef()) {
450 reply_task = std::move(*it);
451 ctx->queue->pending_replies_.erase(it);
452 break;
453 }
454 }
455 }
456 reply_task->Run();
457 break;
458 }
tommic06b1332016-05-14 11:31:40 -0700459 default:
460 RTC_NOTREACHED();
461 break;
462 }
463}
464
465// static
perkj650fdae2017-08-25 05:00:11 -0700466void TaskQueue::Impl::RunTask(int fd, short flags, void* context) { // NOLINT
tommic06b1332016-05-14 11:31:40 -0700467 auto* task = static_cast<QueuedTask*>(context);
468 if (task->Run())
469 delete task;
470}
471
472// static
perkj650fdae2017-08-25 05:00:11 -0700473void TaskQueue::Impl::RunTimer(int fd, short flags, void* context) { // NOLINT
tommic06b1332016-05-14 11:31:40 -0700474 TimerEvent* timer = static_cast<TimerEvent*>(context);
475 if (!timer->task->Run())
476 timer->task.release();
477 QueueContext* ctx =
478 static_cast<QueueContext*>(pthread_getspecific(GetQueuePtrTls()));
479 ctx->pending_timers_.remove(timer);
480 delete timer;
481}
482
perkj650fdae2017-08-25 05:00:11 -0700483void TaskQueue::Impl::PrepareReplyTask(
484 scoped_refptr<ReplyTaskOwnerRef> reply_task) {
tommic06b1332016-05-14 11:31:40 -0700485 RTC_DCHECK(reply_task);
486 CritScope lock(&pending_lock_);
tommi8c80c6e2017-02-23 00:34:52 -0800487 pending_replies_.push_back(std::move(reply_task));
tommic06b1332016-05-14 11:31:40 -0700488}
489
perkj650fdae2017-08-25 05:00:11 -0700490TaskQueue::TaskQueue(const char* queue_name, Priority priority)
491 : impl_(new RefCountedObject<TaskQueue::Impl>(queue_name, this, priority)) {
492}
493
494TaskQueue::~TaskQueue() {}
495
496// static
497TaskQueue* TaskQueue::Current() {
498 return TaskQueue::Impl::CurrentQueue();
499}
500
501// Used for DCHECKing the current queue.
perkj650fdae2017-08-25 05:00:11 -0700502bool TaskQueue::IsCurrent() const {
503 return impl_->IsCurrent();
504}
505
506void TaskQueue::PostTask(std::unique_ptr<QueuedTask> task) {
507 return TaskQueue::impl_->PostTask(std::move(task));
508}
509
510void TaskQueue::PostTaskAndReply(std::unique_ptr<QueuedTask> task,
511 std::unique_ptr<QueuedTask> reply,
512 TaskQueue* reply_queue) {
513 return TaskQueue::impl_->PostTaskAndReply(std::move(task), std::move(reply),
514 reply_queue->impl_.get());
515}
516
517void TaskQueue::PostTaskAndReply(std::unique_ptr<QueuedTask> task,
518 std::unique_ptr<QueuedTask> reply) {
519 return TaskQueue::impl_->PostTaskAndReply(std::move(task), std::move(reply),
520 impl_.get());
521}
522
523void TaskQueue::PostDelayedTask(std::unique_ptr<QueuedTask> task,
524 uint32_t milliseconds) {
525 return TaskQueue::impl_->PostDelayedTask(std::move(task), milliseconds);
526}
527
tommic06b1332016-05-14 11:31:40 -0700528} // namespace rtc