blob: b6e6353798f8d6e68168f41f0ab62f4afc897696 [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"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020027#include "rtc_base/task_queue.h"
28#include "rtc_base/task_queue_posix.h"
29#include "rtc_base/timeutils.h"
tommic06b1332016-05-14 11:31:40 -070030
31namespace rtc {
32using internal::GetQueuePtrTls;
33using internal::AutoSetCurrentQueuePtr;
34
35namespace {
36static const char kQuit = 1;
37static const char kRunTask = 2;
tommi8c80c6e2017-02-23 00:34:52 -080038static const char kRunReplyTask = 3;
39
tommic9bb7912017-02-24 10:42:14 -080040using Priority = TaskQueue::Priority;
41
tommi8c80c6e2017-02-23 00:34:52 -080042// This ignores the SIGPIPE signal on the calling thread.
43// This signal can be fired when trying to write() to a pipe that's being
44// closed or while closing a pipe that's being written to.
45// We can run into that situation (e.g. reply tasks that don't get a chance to
46// run because the task queue is being deleted) so we ignore this signal and
47// continue as normal.
48// As a side note for this implementation, it would be great if we could safely
49// restore the sigmask, but unfortunately the operation of restoring it, can
50// itself actually cause SIGPIPE to be signaled :-| (e.g. on MacOS)
51// The SIGPIPE signal by default causes the process to be terminated, so we
52// don't want to risk that.
53// An alternative to this approach is to ignore the signal for the whole
54// process:
55// signal(SIGPIPE, SIG_IGN);
56void IgnoreSigPipeSignalOnCurrentThread() {
57 sigset_t sigpipe_mask;
58 sigemptyset(&sigpipe_mask);
59 sigaddset(&sigpipe_mask, SIGPIPE);
60 pthread_sigmask(SIG_BLOCK, &sigpipe_mask, nullptr);
61}
tommic06b1332016-05-14 11:31:40 -070062
63struct TimerEvent {
64 explicit TimerEvent(std::unique_ptr<QueuedTask> task)
65 : task(std::move(task)) {}
66 ~TimerEvent() { event_del(&ev); }
67 event ev;
68 std::unique_ptr<QueuedTask> task;
69};
70
71bool SetNonBlocking(int fd) {
72 const int flags = fcntl(fd, F_GETFL);
73 RTC_CHECK(flags != -1);
74 return (flags & O_NONBLOCK) || fcntl(fd, F_SETFL, flags | O_NONBLOCK) != -1;
75}
tommi1666b612016-07-13 10:58:12 -070076
77// TODO(tommi): This is a hack to support two versions of libevent that we're
78// compatible with. The method we really want to call is event_assign(),
79// since event_set() has been marked as deprecated (and doesn't accept
80// passing event_base__ as a parameter). However, the version of libevent
81// that we have in Chromium, doesn't have event_assign(), so we need to call
82// event_set() there.
83void EventAssign(struct event* ev,
84 struct event_base* base,
85 int fd,
86 short events,
87 void (*callback)(int, short, void*),
88 void* arg) {
89#if defined(_EVENT2_EVENT_H_)
90 RTC_CHECK_EQ(0, event_assign(ev, base, fd, events, callback, arg));
91#else
92 event_set(ev, fd, events, callback, arg);
93 RTC_CHECK_EQ(0, event_base_set(base, ev));
94#endif
95}
tommic9bb7912017-02-24 10:42:14 -080096
97ThreadPriority TaskQueuePriorityToThreadPriority(Priority priority) {
98 switch (priority) {
99 case Priority::HIGH:
100 return kRealtimePriority;
101 case Priority::LOW:
102 return kLowPriority;
103 case Priority::NORMAL:
104 return kNormalPriority;
105 default:
106 RTC_NOTREACHED();
107 break;
108 }
109 return kNormalPriority;
110}
tommic06b1332016-05-14 11:31:40 -0700111} // namespace
112
perkj650fdae2017-08-25 05:00:11 -0700113class TaskQueue::Impl : public RefCountInterface {
114 public:
115 explicit Impl(const char* queue_name,
116 TaskQueue* queue,
117 Priority priority = Priority::NORMAL);
118 ~Impl() override;
119
120 static TaskQueue::Impl* Current();
121 static TaskQueue* CurrentQueue();
122
123 // Used for DCHECKing the current queue.
perkj650fdae2017-08-25 05:00:11 -0700124 bool IsCurrent() const;
125
126 void PostTask(std::unique_ptr<QueuedTask> task);
127 void PostTaskAndReply(std::unique_ptr<QueuedTask> task,
128 std::unique_ptr<QueuedTask> reply,
129 TaskQueue::Impl* reply_queue);
130
131 void PostDelayedTask(std::unique_ptr<QueuedTask> task, uint32_t milliseconds);
132
133 private:
134 static void ThreadMain(void* context);
135 static void OnWakeup(int socket, short flags, void* context); // NOLINT
136 static void RunTask(int fd, short flags, void* context); // NOLINT
137 static void RunTimer(int fd, short flags, void* context); // NOLINT
138
139 class ReplyTaskOwner;
140 class PostAndReplyTask;
141 class SetTimerTask;
142
143 typedef RefCountedObject<ReplyTaskOwner> ReplyTaskOwnerRef;
144
145 void PrepareReplyTask(scoped_refptr<ReplyTaskOwnerRef> reply_task);
146
147 struct QueueContext;
148 TaskQueue* const queue_;
149 int wakeup_pipe_in_ = -1;
150 int wakeup_pipe_out_ = -1;
151 event_base* event_base_;
152 std::unique_ptr<event> wakeup_event_;
153 PlatformThread thread_;
154 rtc::CriticalSection pending_lock_;
danilchap3c6abd22017-09-06 05:46:29 -0700155 std::list<std::unique_ptr<QueuedTask>> pending_ RTC_GUARDED_BY(pending_lock_);
perkj650fdae2017-08-25 05:00:11 -0700156 std::list<scoped_refptr<ReplyTaskOwnerRef>> pending_replies_
danilchap3c6abd22017-09-06 05:46:29 -0700157 RTC_GUARDED_BY(pending_lock_);
perkj650fdae2017-08-25 05:00:11 -0700158};
159
160struct TaskQueue::Impl::QueueContext {
161 explicit QueueContext(TaskQueue::Impl* q) : queue(q), is_active(true) {}
162 TaskQueue::Impl* queue;
tommic06b1332016-05-14 11:31:40 -0700163 bool is_active;
164 // Holds a list of events pending timers for cleanup when the loop exits.
165 std::list<TimerEvent*> pending_timers_;
166};
167
tommi8c80c6e2017-02-23 00:34:52 -0800168// Posting a reply task is tricky business. This class owns the reply task
169// and a reference to it is held by both the reply queue and the first task.
170// Here's an outline of what happens when dealing with a reply task.
171// * The ReplyTaskOwner owns the |reply_| task.
172// * One ref owned by PostAndReplyTask
173// * One ref owned by the reply TaskQueue
174// * ReplyTaskOwner has a flag |run_task_| initially set to false.
175// * ReplyTaskOwner has a method: HasOneRef() (provided by RefCountedObject).
176// * After successfully running the original |task_|, PostAndReplyTask() calls
177// set_should_run_task(). This sets |run_task_| to true.
178// * In PostAndReplyTask's dtor:
179// * It releases its reference to ReplyTaskOwner (important to do this first).
180// * Sends (write()) a kRunReplyTask message to the reply queue's pipe.
181// * PostAndReplyTask doesn't care if write() fails, but when it does:
182// * The reply queue is gone.
183// * ReplyTaskOwner has already been deleted and the reply task too.
184// * If write() succeeds:
185// * ReplyQueue receives the kRunReplyTask message
186// * Goes through all pending tasks, finding the first that HasOneRef()
187// * Calls ReplyTaskOwner::Run()
188// * if set_should_run_task() was called, the reply task will be run
189// * Release the reference to ReplyTaskOwner
190// * ReplyTaskOwner and associated |reply_| are deleted.
perkj650fdae2017-08-25 05:00:11 -0700191class TaskQueue::Impl::ReplyTaskOwner {
tommi8c80c6e2017-02-23 00:34:52 -0800192 public:
193 ReplyTaskOwner(std::unique_ptr<QueuedTask> reply)
194 : reply_(std::move(reply)) {}
195
196 void Run() {
197 RTC_DCHECK(reply_);
198 if (run_task_) {
199 if (!reply_->Run())
200 reply_.release();
201 }
202 reply_.reset();
203 }
204
205 void set_should_run_task() {
206 RTC_DCHECK(!run_task_);
207 run_task_ = true;
208 }
209
210 private:
211 std::unique_ptr<QueuedTask> reply_;
212 bool run_task_ = false;
213};
214
perkj650fdae2017-08-25 05:00:11 -0700215class TaskQueue::Impl::PostAndReplyTask : public QueuedTask {
tommic06b1332016-05-14 11:31:40 -0700216 public:
217 PostAndReplyTask(std::unique_ptr<QueuedTask> task,
218 std::unique_ptr<QueuedTask> reply,
perkj650fdae2017-08-25 05:00:11 -0700219 TaskQueue::Impl* reply_queue,
tommi8c80c6e2017-02-23 00:34:52 -0800220 int reply_pipe)
tommic06b1332016-05-14 11:31:40 -0700221 : task_(std::move(task)),
tommi8c80c6e2017-02-23 00:34:52 -0800222 reply_pipe_(reply_pipe),
223 reply_task_owner_(
224 new RefCountedObject<ReplyTaskOwner>(std::move(reply))) {
225 reply_queue->PrepareReplyTask(reply_task_owner_);
tommic06b1332016-05-14 11:31:40 -0700226 }
227
228 ~PostAndReplyTask() override {
tommi8c80c6e2017-02-23 00:34:52 -0800229 reply_task_owner_ = nullptr;
230 IgnoreSigPipeSignalOnCurrentThread();
231 // Send a signal to the reply queue that the reply task can run now.
232 // Depending on whether |set_should_run_task()| was called by the
233 // PostAndReplyTask(), the reply task may or may not actually run.
234 // In either case, it will be deleted.
235 char message = kRunReplyTask;
Tommi79a55602018-02-03 11:17:09 +0100236 RTC_UNUSED(write(reply_pipe_, &message, sizeof(message)));
tommic06b1332016-05-14 11:31:40 -0700237 }
238
239 private:
240 bool Run() override {
241 if (!task_->Run())
242 task_.release();
tommi8c80c6e2017-02-23 00:34:52 -0800243 reply_task_owner_->set_should_run_task();
tommic06b1332016-05-14 11:31:40 -0700244 return true;
245 }
246
tommic06b1332016-05-14 11:31:40 -0700247 std::unique_ptr<QueuedTask> task_;
tommi8c80c6e2017-02-23 00:34:52 -0800248 int reply_pipe_;
249 scoped_refptr<RefCountedObject<ReplyTaskOwner>> reply_task_owner_;
tommic06b1332016-05-14 11:31:40 -0700250};
251
perkj650fdae2017-08-25 05:00:11 -0700252class TaskQueue::Impl::SetTimerTask : public QueuedTask {
tommic06b1332016-05-14 11:31:40 -0700253 public:
254 SetTimerTask(std::unique_ptr<QueuedTask> task, uint32_t milliseconds)
255 : task_(std::move(task)),
256 milliseconds_(milliseconds),
257 posted_(Time32()) {}
258
259 private:
260 bool Run() override {
261 // Compensate for the time that has passed since construction
262 // and until we got here.
263 uint32_t post_time = Time32() - posted_;
perkj650fdae2017-08-25 05:00:11 -0700264 TaskQueue::Impl::Current()->PostDelayedTask(
tommic06b1332016-05-14 11:31:40 -0700265 std::move(task_),
266 post_time > milliseconds_ ? 0 : milliseconds_ - post_time);
267 return true;
268 }
269
270 std::unique_ptr<QueuedTask> task_;
271 const uint32_t milliseconds_;
272 const uint32_t posted_;
273};
274
perkj650fdae2017-08-25 05:00:11 -0700275TaskQueue::Impl::Impl(const char* queue_name,
276 TaskQueue* queue,
277 Priority priority /*= NORMAL*/)
278 : queue_(queue),
279 event_base_(event_base_new()),
tommic06b1332016-05-14 11:31:40 -0700280 wakeup_event_(new event()),
perkj650fdae2017-08-25 05:00:11 -0700281 thread_(&TaskQueue::Impl::ThreadMain,
tommic9bb7912017-02-24 10:42:14 -0800282 this,
283 queue_name,
284 TaskQueuePriorityToThreadPriority(priority)) {
tommic06b1332016-05-14 11:31:40 -0700285 RTC_DCHECK(queue_name);
286 int fds[2];
287 RTC_CHECK(pipe(fds) == 0);
288 SetNonBlocking(fds[0]);
289 SetNonBlocking(fds[1]);
290 wakeup_pipe_out_ = fds[0];
291 wakeup_pipe_in_ = fds[1];
tommi8c80c6e2017-02-23 00:34:52 -0800292
tommi1666b612016-07-13 10:58:12 -0700293 EventAssign(wakeup_event_.get(), event_base_, wakeup_pipe_out_,
294 EV_READ | EV_PERSIST, OnWakeup, this);
tommic06b1332016-05-14 11:31:40 -0700295 event_add(wakeup_event_.get(), 0);
296 thread_.Start();
297}
298
perkj650fdae2017-08-25 05:00:11 -0700299TaskQueue::Impl::~Impl() {
tommic06b1332016-05-14 11:31:40 -0700300 RTC_DCHECK(!IsCurrent());
301 struct timespec ts;
302 char message = kQuit;
303 while (write(wakeup_pipe_in_, &message, sizeof(message)) != sizeof(message)) {
304 // The queue is full, so we have no choice but to wait and retry.
305 RTC_CHECK_EQ(EAGAIN, errno);
306 ts.tv_sec = 0;
307 ts.tv_nsec = 1000000;
308 nanosleep(&ts, nullptr);
309 }
310
311 thread_.Stop();
312
313 event_del(wakeup_event_.get());
tommi8c80c6e2017-02-23 00:34:52 -0800314
315 IgnoreSigPipeSignalOnCurrentThread();
316
tommic06b1332016-05-14 11:31:40 -0700317 close(wakeup_pipe_in_);
318 close(wakeup_pipe_out_);
319 wakeup_pipe_in_ = -1;
320 wakeup_pipe_out_ = -1;
321
tommic06b1332016-05-14 11:31:40 -0700322 event_base_free(event_base_);
323}
324
325// static
perkj650fdae2017-08-25 05:00:11 -0700326TaskQueue::Impl* TaskQueue::Impl::Current() {
tommic06b1332016-05-14 11:31:40 -0700327 QueueContext* ctx =
328 static_cast<QueueContext*>(pthread_getspecific(GetQueuePtrTls()));
329 return ctx ? ctx->queue : nullptr;
330}
331
332// static
perkj650fdae2017-08-25 05:00:11 -0700333TaskQueue* TaskQueue::Impl::CurrentQueue() {
334 TaskQueue::Impl* current = Current();
335 if (current) {
336 return current->queue_;
337 }
338 return nullptr;
339}
340
perkj650fdae2017-08-25 05:00:11 -0700341bool TaskQueue::Impl::IsCurrent() const {
tommic06b1332016-05-14 11:31:40 -0700342 return IsThreadRefEqual(thread_.GetThreadRef(), CurrentThreadRef());
343}
344
perkj650fdae2017-08-25 05:00:11 -0700345void TaskQueue::Impl::PostTask(std::unique_ptr<QueuedTask> task) {
tommic06b1332016-05-14 11:31:40 -0700346 RTC_DCHECK(task.get());
347 // libevent isn't thread safe. This means that we can't use methods such
348 // as event_base_once to post tasks to the worker thread from a different
349 // thread. However, we can use it when posting from the worker thread itself.
350 if (IsCurrent()) {
perkj650fdae2017-08-25 05:00:11 -0700351 if (event_base_once(event_base_, -1, EV_TIMEOUT, &TaskQueue::Impl::RunTask,
tommic06b1332016-05-14 11:31:40 -0700352 task.get(), nullptr) == 0) {
353 task.release();
354 }
355 } else {
356 QueuedTask* task_id = task.get(); // Only used for comparison.
357 {
358 CritScope lock(&pending_lock_);
359 pending_.push_back(std::move(task));
360 }
361 char message = kRunTask;
362 if (write(wakeup_pipe_in_, &message, sizeof(message)) != sizeof(message)) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100363 RTC_LOG(WARNING) << "Failed to queue task.";
tommic06b1332016-05-14 11:31:40 -0700364 CritScope lock(&pending_lock_);
365 pending_.remove_if([task_id](std::unique_ptr<QueuedTask>& t) {
366 return t.get() == task_id;
367 });
368 }
369 }
370}
371
perkj650fdae2017-08-25 05:00:11 -0700372void TaskQueue::Impl::PostDelayedTask(std::unique_ptr<QueuedTask> task,
373 uint32_t milliseconds) {
tommic06b1332016-05-14 11:31:40 -0700374 if (IsCurrent()) {
375 TimerEvent* timer = new TimerEvent(std::move(task));
perkj650fdae2017-08-25 05:00:11 -0700376 EventAssign(&timer->ev, event_base_, -1, 0, &TaskQueue::Impl::RunTimer,
377 timer);
tommic06b1332016-05-14 11:31:40 -0700378 QueueContext* ctx =
379 static_cast<QueueContext*>(pthread_getspecific(GetQueuePtrTls()));
380 ctx->pending_timers_.push_back(timer);
kwiberg5b9746e2017-08-16 04:52:35 -0700381 timeval tv = {rtc::dchecked_cast<int>(milliseconds / 1000),
382 rtc::dchecked_cast<int>(milliseconds % 1000) * 1000};
tommic06b1332016-05-14 11:31:40 -0700383 event_add(&timer->ev, &tv);
384 } else {
385 PostTask(std::unique_ptr<QueuedTask>(
386 new SetTimerTask(std::move(task), milliseconds)));
387 }
388}
389
perkj650fdae2017-08-25 05:00:11 -0700390void TaskQueue::Impl::PostTaskAndReply(std::unique_ptr<QueuedTask> task,
391 std::unique_ptr<QueuedTask> reply,
392 TaskQueue::Impl* reply_queue) {
tommic06b1332016-05-14 11:31:40 -0700393 std::unique_ptr<QueuedTask> wrapper_task(
tommi8c80c6e2017-02-23 00:34:52 -0800394 new PostAndReplyTask(std::move(task), std::move(reply), reply_queue,
395 reply_queue->wakeup_pipe_in_));
tommic06b1332016-05-14 11:31:40 -0700396 PostTask(std::move(wrapper_task));
397}
398
tommic06b1332016-05-14 11:31:40 -0700399// static
perkj650fdae2017-08-25 05:00:11 -0700400void TaskQueue::Impl::ThreadMain(void* context) {
401 TaskQueue::Impl* me = static_cast<TaskQueue::Impl*>(context);
tommic06b1332016-05-14 11:31:40 -0700402
403 QueueContext queue_context(me);
404 pthread_setspecific(GetQueuePtrTls(), &queue_context);
405
406 while (queue_context.is_active)
407 event_base_loop(me->event_base_, 0);
408
409 pthread_setspecific(GetQueuePtrTls(), nullptr);
410
411 for (TimerEvent* timer : queue_context.pending_timers_)
412 delete timer;
tommic06b1332016-05-14 11:31:40 -0700413}
414
415// static
perkj650fdae2017-08-25 05:00:11 -0700416void TaskQueue::Impl::OnWakeup(int socket,
417 short flags,
418 void* context) { // NOLINT
tommic06b1332016-05-14 11:31:40 -0700419 QueueContext* ctx =
420 static_cast<QueueContext*>(pthread_getspecific(GetQueuePtrTls()));
421 RTC_DCHECK(ctx->queue->wakeup_pipe_out_ == socket);
422 char buf;
423 RTC_CHECK(sizeof(buf) == read(socket, &buf, sizeof(buf)));
424 switch (buf) {
425 case kQuit:
426 ctx->is_active = false;
427 event_base_loopbreak(ctx->queue->event_base_);
428 break;
429 case kRunTask: {
430 std::unique_ptr<QueuedTask> task;
431 {
432 CritScope lock(&ctx->queue->pending_lock_);
433 RTC_DCHECK(!ctx->queue->pending_.empty());
434 task = std::move(ctx->queue->pending_.front());
435 ctx->queue->pending_.pop_front();
436 RTC_DCHECK(task.get());
437 }
438 if (!task->Run())
439 task.release();
440 break;
441 }
tommi8c80c6e2017-02-23 00:34:52 -0800442 case kRunReplyTask: {
443 scoped_refptr<ReplyTaskOwnerRef> reply_task;
444 {
445 CritScope lock(&ctx->queue->pending_lock_);
446 for (auto it = ctx->queue->pending_replies_.begin();
447 it != ctx->queue->pending_replies_.end(); ++it) {
448 if ((*it)->HasOneRef()) {
449 reply_task = std::move(*it);
450 ctx->queue->pending_replies_.erase(it);
451 break;
452 }
453 }
454 }
455 reply_task->Run();
456 break;
457 }
tommic06b1332016-05-14 11:31:40 -0700458 default:
459 RTC_NOTREACHED();
460 break;
461 }
462}
463
464// static
perkj650fdae2017-08-25 05:00:11 -0700465void TaskQueue::Impl::RunTask(int fd, short flags, void* context) { // NOLINT
tommic06b1332016-05-14 11:31:40 -0700466 auto* task = static_cast<QueuedTask*>(context);
467 if (task->Run())
468 delete task;
469}
470
471// static
perkj650fdae2017-08-25 05:00:11 -0700472void TaskQueue::Impl::RunTimer(int fd, short flags, void* context) { // NOLINT
tommic06b1332016-05-14 11:31:40 -0700473 TimerEvent* timer = static_cast<TimerEvent*>(context);
474 if (!timer->task->Run())
475 timer->task.release();
476 QueueContext* ctx =
477 static_cast<QueueContext*>(pthread_getspecific(GetQueuePtrTls()));
478 ctx->pending_timers_.remove(timer);
479 delete timer;
480}
481
perkj650fdae2017-08-25 05:00:11 -0700482void TaskQueue::Impl::PrepareReplyTask(
483 scoped_refptr<ReplyTaskOwnerRef> reply_task) {
tommic06b1332016-05-14 11:31:40 -0700484 RTC_DCHECK(reply_task);
485 CritScope lock(&pending_lock_);
tommi8c80c6e2017-02-23 00:34:52 -0800486 pending_replies_.push_back(std::move(reply_task));
tommic06b1332016-05-14 11:31:40 -0700487}
488
perkj650fdae2017-08-25 05:00:11 -0700489TaskQueue::TaskQueue(const char* queue_name, Priority priority)
490 : impl_(new RefCountedObject<TaskQueue::Impl>(queue_name, this, priority)) {
491}
492
493TaskQueue::~TaskQueue() {}
494
495// static
496TaskQueue* TaskQueue::Current() {
497 return TaskQueue::Impl::CurrentQueue();
498}
499
500// Used for DCHECKing the current queue.
perkj650fdae2017-08-25 05:00:11 -0700501bool TaskQueue::IsCurrent() const {
502 return impl_->IsCurrent();
503}
504
505void TaskQueue::PostTask(std::unique_ptr<QueuedTask> task) {
506 return TaskQueue::impl_->PostTask(std::move(task));
507}
508
509void TaskQueue::PostTaskAndReply(std::unique_ptr<QueuedTask> task,
510 std::unique_ptr<QueuedTask> reply,
511 TaskQueue* reply_queue) {
512 return TaskQueue::impl_->PostTaskAndReply(std::move(task), std::move(reply),
513 reply_queue->impl_.get());
514}
515
516void TaskQueue::PostTaskAndReply(std::unique_ptr<QueuedTask> task,
517 std::unique_ptr<QueuedTask> reply) {
518 return TaskQueue::impl_->PostTaskAndReply(std::move(task), std::move(reply),
519 impl_.get());
520}
521
522void TaskQueue::PostDelayedTask(std::unique_ptr<QueuedTask> task,
523 uint32_t milliseconds) {
524 return TaskQueue::impl_->PostDelayedTask(std::move(task), milliseconds);
525}
526
tommic06b1332016-05-14 11:31:40 -0700527} // namespace rtc