blob: 905bbdac0e2e5a1bedd1e64affeef9d0831d8fd2 [file] [log] [blame]
tommic06b1332016-05-14 18:31:401/*
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 04:47:3111#include "rtc_base/task_queue.h"
tommic06b1332016-05-14 18:31:4012
Yves Gerey988cc082018-10-23 10:03:0113#include <errno.h>
tommic06b1332016-05-14 18:31:4014#include <fcntl.h>
Yves Gerey988cc082018-10-23 10:03:0115#include <pthread.h>
tommi8c80c6e2017-02-23 08:34:5216#include <signal.h>
Yves Gerey988cc082018-10-23 10:03:0117#include <stdint.h>
18#include <time.h>
tommic06b1332016-05-14 18:31:4019#include <unistd.h>
Danil Chapovalov02fddf62018-02-12 11:41:1620#include <list>
Yves Gerey988cc082018-10-23 10:03:0121#include <memory>
22#include <type_traits>
23#include <utility>
tommic06b1332016-05-14 18:31:4024
25#include "base/third_party/libevent/event.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3126#include "rtc_base/checks.h"
Danil Chapovalov02fddf62018-02-12 11:41:1627#include "rtc_base/criticalsection.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3128#include "rtc_base/logging.h"
Karl Wiberge40468b2017-11-22 09:42:2629#include "rtc_base/numerics/safe_conversions.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3130#include "rtc_base/platform_thread.h"
Yves Gerey988cc082018-10-23 10:03:0131#include "rtc_base/platform_thread_types.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3132#include "rtc_base/refcount.h"
33#include "rtc_base/refcountedobject.h"
Yves Gerey988cc082018-10-23 10:03:0134#include "rtc_base/scoped_ref_ptr.h"
Niels Möllera12c42a2018-07-25 14:05:4835#include "rtc_base/system/unused.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3136#include "rtc_base/task_queue_posix.h"
Yves Gerey988cc082018-10-23 10:03:0137#include "rtc_base/thread_annotations.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3138#include "rtc_base/timeutils.h"
tommic06b1332016-05-14 18:31:4039
40namespace rtc {
41using internal::GetQueuePtrTls;
42using internal::AutoSetCurrentQueuePtr;
43
44namespace {
45static const char kQuit = 1;
46static const char kRunTask = 2;
tommi8c80c6e2017-02-23 08:34:5247static const char kRunReplyTask = 3;
48
tommic9bb7912017-02-24 18:42:1449using Priority = TaskQueue::Priority;
50
tommi8c80c6e2017-02-23 08:34:5251// This ignores the SIGPIPE signal on the calling thread.
52// This signal can be fired when trying to write() to a pipe that's being
53// closed or while closing a pipe that's being written to.
54// We can run into that situation (e.g. reply tasks that don't get a chance to
55// run because the task queue is being deleted) so we ignore this signal and
56// continue as normal.
57// As a side note for this implementation, it would be great if we could safely
58// restore the sigmask, but unfortunately the operation of restoring it, can
59// itself actually cause SIGPIPE to be signaled :-| (e.g. on MacOS)
60// The SIGPIPE signal by default causes the process to be terminated, so we
61// don't want to risk that.
62// An alternative to this approach is to ignore the signal for the whole
63// process:
64// signal(SIGPIPE, SIG_IGN);
65void IgnoreSigPipeSignalOnCurrentThread() {
66 sigset_t sigpipe_mask;
67 sigemptyset(&sigpipe_mask);
68 sigaddset(&sigpipe_mask, SIGPIPE);
69 pthread_sigmask(SIG_BLOCK, &sigpipe_mask, nullptr);
70}
tommic06b1332016-05-14 18:31:4071
72struct TimerEvent {
73 explicit TimerEvent(std::unique_ptr<QueuedTask> task)
74 : task(std::move(task)) {}
75 ~TimerEvent() { event_del(&ev); }
76 event ev;
77 std::unique_ptr<QueuedTask> task;
78};
79
80bool SetNonBlocking(int fd) {
81 const int flags = fcntl(fd, F_GETFL);
82 RTC_CHECK(flags != -1);
83 return (flags & O_NONBLOCK) || fcntl(fd, F_SETFL, flags | O_NONBLOCK) != -1;
84}
tommi1666b612016-07-13 17:58:1285
86// TODO(tommi): This is a hack to support two versions of libevent that we're
87// compatible with. The method we really want to call is event_assign(),
88// since event_set() has been marked as deprecated (and doesn't accept
89// passing event_base__ as a parameter). However, the version of libevent
90// that we have in Chromium, doesn't have event_assign(), so we need to call
91// event_set() there.
92void EventAssign(struct event* ev,
93 struct event_base* base,
94 int fd,
95 short events,
96 void (*callback)(int, short, void*),
97 void* arg) {
98#if defined(_EVENT2_EVENT_H_)
99 RTC_CHECK_EQ(0, event_assign(ev, base, fd, events, callback, arg));
100#else
101 event_set(ev, fd, events, callback, arg);
102 RTC_CHECK_EQ(0, event_base_set(base, ev));
103#endif
104}
tommic9bb7912017-02-24 18:42:14105
106ThreadPriority TaskQueuePriorityToThreadPriority(Priority priority) {
107 switch (priority) {
108 case Priority::HIGH:
109 return kRealtimePriority;
110 case Priority::LOW:
111 return kLowPriority;
112 case Priority::NORMAL:
113 return kNormalPriority;
114 default:
115 RTC_NOTREACHED();
116 break;
117 }
118 return kNormalPriority;
119}
tommic06b1332016-05-14 18:31:40120} // namespace
121
perkj650fdae2017-08-25 12:00:11122class TaskQueue::Impl : public RefCountInterface {
123 public:
124 explicit Impl(const char* queue_name,
125 TaskQueue* queue,
126 Priority priority = Priority::NORMAL);
127 ~Impl() override;
128
129 static TaskQueue::Impl* Current();
130 static TaskQueue* CurrentQueue();
131
132 // Used for DCHECKing the current queue.
perkj650fdae2017-08-25 12:00:11133 bool IsCurrent() const;
134
135 void PostTask(std::unique_ptr<QueuedTask> task);
136 void PostTaskAndReply(std::unique_ptr<QueuedTask> task,
137 std::unique_ptr<QueuedTask> reply,
138 TaskQueue::Impl* reply_queue);
139
140 void PostDelayedTask(std::unique_ptr<QueuedTask> task, uint32_t milliseconds);
141
142 private:
143 static void ThreadMain(void* context);
144 static void OnWakeup(int socket, short flags, void* context); // NOLINT
145 static void RunTask(int fd, short flags, void* context); // NOLINT
146 static void RunTimer(int fd, short flags, void* context); // NOLINT
147
148 class ReplyTaskOwner;
149 class PostAndReplyTask;
150 class SetTimerTask;
151
152 typedef RefCountedObject<ReplyTaskOwner> ReplyTaskOwnerRef;
153
154 void PrepareReplyTask(scoped_refptr<ReplyTaskOwnerRef> reply_task);
155
156 struct QueueContext;
157 TaskQueue* const queue_;
158 int wakeup_pipe_in_ = -1;
159 int wakeup_pipe_out_ = -1;
160 event_base* event_base_;
161 std::unique_ptr<event> wakeup_event_;
162 PlatformThread thread_;
163 rtc::CriticalSection pending_lock_;
danilchap3c6abd22017-09-06 12:46:29164 std::list<std::unique_ptr<QueuedTask>> pending_ RTC_GUARDED_BY(pending_lock_);
perkj650fdae2017-08-25 12:00:11165 std::list<scoped_refptr<ReplyTaskOwnerRef>> pending_replies_
danilchap3c6abd22017-09-06 12:46:29166 RTC_GUARDED_BY(pending_lock_);
perkj650fdae2017-08-25 12:00:11167};
168
169struct TaskQueue::Impl::QueueContext {
170 explicit QueueContext(TaskQueue::Impl* q) : queue(q), is_active(true) {}
171 TaskQueue::Impl* queue;
tommic06b1332016-05-14 18:31:40172 bool is_active;
173 // Holds a list of events pending timers for cleanup when the loop exits.
174 std::list<TimerEvent*> pending_timers_;
175};
176
tommi8c80c6e2017-02-23 08:34:52177// Posting a reply task is tricky business. This class owns the reply task
178// and a reference to it is held by both the reply queue and the first task.
179// Here's an outline of what happens when dealing with a reply task.
180// * The ReplyTaskOwner owns the |reply_| task.
181// * One ref owned by PostAndReplyTask
182// * One ref owned by the reply TaskQueue
183// * ReplyTaskOwner has a flag |run_task_| initially set to false.
184// * ReplyTaskOwner has a method: HasOneRef() (provided by RefCountedObject).
185// * After successfully running the original |task_|, PostAndReplyTask() calls
186// set_should_run_task(). This sets |run_task_| to true.
187// * In PostAndReplyTask's dtor:
188// * It releases its reference to ReplyTaskOwner (important to do this first).
189// * Sends (write()) a kRunReplyTask message to the reply queue's pipe.
190// * PostAndReplyTask doesn't care if write() fails, but when it does:
191// * The reply queue is gone.
192// * ReplyTaskOwner has already been deleted and the reply task too.
193// * If write() succeeds:
194// * ReplyQueue receives the kRunReplyTask message
195// * Goes through all pending tasks, finding the first that HasOneRef()
196// * Calls ReplyTaskOwner::Run()
197// * if set_should_run_task() was called, the reply task will be run
198// * Release the reference to ReplyTaskOwner
199// * ReplyTaskOwner and associated |reply_| are deleted.
perkj650fdae2017-08-25 12:00:11200class TaskQueue::Impl::ReplyTaskOwner {
tommi8c80c6e2017-02-23 08:34:52201 public:
202 ReplyTaskOwner(std::unique_ptr<QueuedTask> reply)
203 : reply_(std::move(reply)) {}
204
205 void Run() {
206 RTC_DCHECK(reply_);
207 if (run_task_) {
208 if (!reply_->Run())
209 reply_.release();
210 }
211 reply_.reset();
212 }
213
214 void set_should_run_task() {
215 RTC_DCHECK(!run_task_);
216 run_task_ = true;
217 }
218
219 private:
220 std::unique_ptr<QueuedTask> reply_;
221 bool run_task_ = false;
222};
223
perkj650fdae2017-08-25 12:00:11224class TaskQueue::Impl::PostAndReplyTask : public QueuedTask {
tommic06b1332016-05-14 18:31:40225 public:
226 PostAndReplyTask(std::unique_ptr<QueuedTask> task,
227 std::unique_ptr<QueuedTask> reply,
perkj650fdae2017-08-25 12:00:11228 TaskQueue::Impl* reply_queue,
tommi8c80c6e2017-02-23 08:34:52229 int reply_pipe)
tommic06b1332016-05-14 18:31:40230 : task_(std::move(task)),
tommi8c80c6e2017-02-23 08:34:52231 reply_pipe_(reply_pipe),
232 reply_task_owner_(
233 new RefCountedObject<ReplyTaskOwner>(std::move(reply))) {
234 reply_queue->PrepareReplyTask(reply_task_owner_);
tommic06b1332016-05-14 18:31:40235 }
236
237 ~PostAndReplyTask() override {
tommi8c80c6e2017-02-23 08:34:52238 reply_task_owner_ = nullptr;
239 IgnoreSigPipeSignalOnCurrentThread();
240 // Send a signal to the reply queue that the reply task can run now.
241 // Depending on whether |set_should_run_task()| was called by the
242 // PostAndReplyTask(), the reply task may or may not actually run.
243 // In either case, it will be deleted.
244 char message = kRunReplyTask;
Tommi79a55602018-02-03 10:17:09245 RTC_UNUSED(write(reply_pipe_, &message, sizeof(message)));
tommic06b1332016-05-14 18:31:40246 }
247
248 private:
249 bool Run() override {
250 if (!task_->Run())
251 task_.release();
tommi8c80c6e2017-02-23 08:34:52252 reply_task_owner_->set_should_run_task();
tommic06b1332016-05-14 18:31:40253 return true;
254 }
255
tommic06b1332016-05-14 18:31:40256 std::unique_ptr<QueuedTask> task_;
tommi8c80c6e2017-02-23 08:34:52257 int reply_pipe_;
258 scoped_refptr<RefCountedObject<ReplyTaskOwner>> reply_task_owner_;
tommic06b1332016-05-14 18:31:40259};
260
perkj650fdae2017-08-25 12:00:11261class TaskQueue::Impl::SetTimerTask : public QueuedTask {
tommic06b1332016-05-14 18:31:40262 public:
263 SetTimerTask(std::unique_ptr<QueuedTask> task, uint32_t milliseconds)
264 : task_(std::move(task)),
265 milliseconds_(milliseconds),
266 posted_(Time32()) {}
267
268 private:
269 bool Run() override {
270 // Compensate for the time that has passed since construction
271 // and until we got here.
272 uint32_t post_time = Time32() - posted_;
perkj650fdae2017-08-25 12:00:11273 TaskQueue::Impl::Current()->PostDelayedTask(
tommic06b1332016-05-14 18:31:40274 std::move(task_),
275 post_time > milliseconds_ ? 0 : milliseconds_ - post_time);
276 return true;
277 }
278
279 std::unique_ptr<QueuedTask> task_;
280 const uint32_t milliseconds_;
281 const uint32_t posted_;
282};
283
perkj650fdae2017-08-25 12:00:11284TaskQueue::Impl::Impl(const char* queue_name,
285 TaskQueue* queue,
286 Priority priority /*= NORMAL*/)
287 : queue_(queue),
288 event_base_(event_base_new()),
tommic06b1332016-05-14 18:31:40289 wakeup_event_(new event()),
perkj650fdae2017-08-25 12:00:11290 thread_(&TaskQueue::Impl::ThreadMain,
tommic9bb7912017-02-24 18:42:14291 this,
292 queue_name,
293 TaskQueuePriorityToThreadPriority(priority)) {
tommic06b1332016-05-14 18:31:40294 RTC_DCHECK(queue_name);
295 int fds[2];
296 RTC_CHECK(pipe(fds) == 0);
297 SetNonBlocking(fds[0]);
298 SetNonBlocking(fds[1]);
299 wakeup_pipe_out_ = fds[0];
300 wakeup_pipe_in_ = fds[1];
tommi8c80c6e2017-02-23 08:34:52301
tommi1666b612016-07-13 17:58:12302 EventAssign(wakeup_event_.get(), event_base_, wakeup_pipe_out_,
303 EV_READ | EV_PERSIST, OnWakeup, this);
tommic06b1332016-05-14 18:31:40304 event_add(wakeup_event_.get(), 0);
305 thread_.Start();
306}
307
perkj650fdae2017-08-25 12:00:11308TaskQueue::Impl::~Impl() {
tommic06b1332016-05-14 18:31:40309 RTC_DCHECK(!IsCurrent());
310 struct timespec ts;
311 char message = kQuit;
312 while (write(wakeup_pipe_in_, &message, sizeof(message)) != sizeof(message)) {
313 // The queue is full, so we have no choice but to wait and retry.
314 RTC_CHECK_EQ(EAGAIN, errno);
315 ts.tv_sec = 0;
316 ts.tv_nsec = 1000000;
317 nanosleep(&ts, nullptr);
318 }
319
320 thread_.Stop();
321
322 event_del(wakeup_event_.get());
tommi8c80c6e2017-02-23 08:34:52323
324 IgnoreSigPipeSignalOnCurrentThread();
325
tommic06b1332016-05-14 18:31:40326 close(wakeup_pipe_in_);
327 close(wakeup_pipe_out_);
328 wakeup_pipe_in_ = -1;
329 wakeup_pipe_out_ = -1;
330
tommic06b1332016-05-14 18:31:40331 event_base_free(event_base_);
332}
333
334// static
perkj650fdae2017-08-25 12:00:11335TaskQueue::Impl* TaskQueue::Impl::Current() {
tommic06b1332016-05-14 18:31:40336 QueueContext* ctx =
337 static_cast<QueueContext*>(pthread_getspecific(GetQueuePtrTls()));
338 return ctx ? ctx->queue : nullptr;
339}
340
341// static
perkj650fdae2017-08-25 12:00:11342TaskQueue* TaskQueue::Impl::CurrentQueue() {
343 TaskQueue::Impl* current = Current();
344 if (current) {
345 return current->queue_;
346 }
347 return nullptr;
348}
349
perkj650fdae2017-08-25 12:00:11350bool TaskQueue::Impl::IsCurrent() const {
tommic06b1332016-05-14 18:31:40351 return IsThreadRefEqual(thread_.GetThreadRef(), CurrentThreadRef());
352}
353
perkj650fdae2017-08-25 12:00:11354void TaskQueue::Impl::PostTask(std::unique_ptr<QueuedTask> task) {
tommic06b1332016-05-14 18:31:40355 RTC_DCHECK(task.get());
356 // libevent isn't thread safe. This means that we can't use methods such
357 // as event_base_once to post tasks to the worker thread from a different
358 // thread. However, we can use it when posting from the worker thread itself.
359 if (IsCurrent()) {
perkj650fdae2017-08-25 12:00:11360 if (event_base_once(event_base_, -1, EV_TIMEOUT, &TaskQueue::Impl::RunTask,
tommic06b1332016-05-14 18:31:40361 task.get(), nullptr) == 0) {
362 task.release();
363 }
364 } else {
365 QueuedTask* task_id = task.get(); // Only used for comparison.
366 {
367 CritScope lock(&pending_lock_);
368 pending_.push_back(std::move(task));
369 }
370 char message = kRunTask;
371 if (write(wakeup_pipe_in_, &message, sizeof(message)) != sizeof(message)) {
Mirko Bonadei675513b2017-11-09 10:09:25372 RTC_LOG(WARNING) << "Failed to queue task.";
tommic06b1332016-05-14 18:31:40373 CritScope lock(&pending_lock_);
374 pending_.remove_if([task_id](std::unique_ptr<QueuedTask>& t) {
375 return t.get() == task_id;
376 });
377 }
378 }
379}
380
perkj650fdae2017-08-25 12:00:11381void TaskQueue::Impl::PostDelayedTask(std::unique_ptr<QueuedTask> task,
382 uint32_t milliseconds) {
tommic06b1332016-05-14 18:31:40383 if (IsCurrent()) {
384 TimerEvent* timer = new TimerEvent(std::move(task));
perkj650fdae2017-08-25 12:00:11385 EventAssign(&timer->ev, event_base_, -1, 0, &TaskQueue::Impl::RunTimer,
386 timer);
tommic06b1332016-05-14 18:31:40387 QueueContext* ctx =
388 static_cast<QueueContext*>(pthread_getspecific(GetQueuePtrTls()));
389 ctx->pending_timers_.push_back(timer);
kwiberg5b9746e2017-08-16 11:52:35390 timeval tv = {rtc::dchecked_cast<int>(milliseconds / 1000),
391 rtc::dchecked_cast<int>(milliseconds % 1000) * 1000};
tommic06b1332016-05-14 18:31:40392 event_add(&timer->ev, &tv);
393 } else {
394 PostTask(std::unique_ptr<QueuedTask>(
395 new SetTimerTask(std::move(task), milliseconds)));
396 }
397}
398
perkj650fdae2017-08-25 12:00:11399void TaskQueue::Impl::PostTaskAndReply(std::unique_ptr<QueuedTask> task,
400 std::unique_ptr<QueuedTask> reply,
401 TaskQueue::Impl* reply_queue) {
tommic06b1332016-05-14 18:31:40402 std::unique_ptr<QueuedTask> wrapper_task(
tommi8c80c6e2017-02-23 08:34:52403 new PostAndReplyTask(std::move(task), std::move(reply), reply_queue,
404 reply_queue->wakeup_pipe_in_));
tommic06b1332016-05-14 18:31:40405 PostTask(std::move(wrapper_task));
406}
407
tommic06b1332016-05-14 18:31:40408// static
perkj650fdae2017-08-25 12:00:11409void TaskQueue::Impl::ThreadMain(void* context) {
410 TaskQueue::Impl* me = static_cast<TaskQueue::Impl*>(context);
tommic06b1332016-05-14 18:31:40411
412 QueueContext queue_context(me);
413 pthread_setspecific(GetQueuePtrTls(), &queue_context);
414
415 while (queue_context.is_active)
416 event_base_loop(me->event_base_, 0);
417
418 pthread_setspecific(GetQueuePtrTls(), nullptr);
419
420 for (TimerEvent* timer : queue_context.pending_timers_)
421 delete timer;
tommic06b1332016-05-14 18:31:40422}
423
424// static
perkj650fdae2017-08-25 12:00:11425void TaskQueue::Impl::OnWakeup(int socket,
426 short flags,
427 void* context) { // NOLINT
tommic06b1332016-05-14 18:31:40428 QueueContext* ctx =
429 static_cast<QueueContext*>(pthread_getspecific(GetQueuePtrTls()));
430 RTC_DCHECK(ctx->queue->wakeup_pipe_out_ == socket);
431 char buf;
432 RTC_CHECK(sizeof(buf) == read(socket, &buf, sizeof(buf)));
433 switch (buf) {
434 case kQuit:
435 ctx->is_active = false;
436 event_base_loopbreak(ctx->queue->event_base_);
437 break;
438 case kRunTask: {
439 std::unique_ptr<QueuedTask> task;
440 {
441 CritScope lock(&ctx->queue->pending_lock_);
442 RTC_DCHECK(!ctx->queue->pending_.empty());
443 task = std::move(ctx->queue->pending_.front());
444 ctx->queue->pending_.pop_front();
445 RTC_DCHECK(task.get());
446 }
447 if (!task->Run())
448 task.release();
449 break;
450 }
tommi8c80c6e2017-02-23 08:34:52451 case kRunReplyTask: {
452 scoped_refptr<ReplyTaskOwnerRef> reply_task;
453 {
454 CritScope lock(&ctx->queue->pending_lock_);
455 for (auto it = ctx->queue->pending_replies_.begin();
456 it != ctx->queue->pending_replies_.end(); ++it) {
457 if ((*it)->HasOneRef()) {
458 reply_task = std::move(*it);
459 ctx->queue->pending_replies_.erase(it);
460 break;
461 }
462 }
463 }
464 reply_task->Run();
465 break;
466 }
tommic06b1332016-05-14 18:31:40467 default:
468 RTC_NOTREACHED();
469 break;
470 }
471}
472
473// static
perkj650fdae2017-08-25 12:00:11474void TaskQueue::Impl::RunTask(int fd, short flags, void* context) { // NOLINT
tommic06b1332016-05-14 18:31:40475 auto* task = static_cast<QueuedTask*>(context);
476 if (task->Run())
477 delete task;
478}
479
480// static
perkj650fdae2017-08-25 12:00:11481void TaskQueue::Impl::RunTimer(int fd, short flags, void* context) { // NOLINT
tommic06b1332016-05-14 18:31:40482 TimerEvent* timer = static_cast<TimerEvent*>(context);
483 if (!timer->task->Run())
484 timer->task.release();
485 QueueContext* ctx =
486 static_cast<QueueContext*>(pthread_getspecific(GetQueuePtrTls()));
487 ctx->pending_timers_.remove(timer);
488 delete timer;
489}
490
perkj650fdae2017-08-25 12:00:11491void TaskQueue::Impl::PrepareReplyTask(
492 scoped_refptr<ReplyTaskOwnerRef> reply_task) {
tommic06b1332016-05-14 18:31:40493 RTC_DCHECK(reply_task);
494 CritScope lock(&pending_lock_);
tommi8c80c6e2017-02-23 08:34:52495 pending_replies_.push_back(std::move(reply_task));
tommic06b1332016-05-14 18:31:40496}
497
perkj650fdae2017-08-25 12:00:11498TaskQueue::TaskQueue(const char* queue_name, Priority priority)
499 : impl_(new RefCountedObject<TaskQueue::Impl>(queue_name, this, priority)) {
500}
501
502TaskQueue::~TaskQueue() {}
503
504// static
505TaskQueue* TaskQueue::Current() {
506 return TaskQueue::Impl::CurrentQueue();
507}
508
509// Used for DCHECKing the current queue.
perkj650fdae2017-08-25 12:00:11510bool TaskQueue::IsCurrent() const {
511 return impl_->IsCurrent();
512}
513
514void TaskQueue::PostTask(std::unique_ptr<QueuedTask> task) {
515 return TaskQueue::impl_->PostTask(std::move(task));
516}
517
518void TaskQueue::PostTaskAndReply(std::unique_ptr<QueuedTask> task,
519 std::unique_ptr<QueuedTask> reply,
520 TaskQueue* reply_queue) {
521 return TaskQueue::impl_->PostTaskAndReply(std::move(task), std::move(reply),
522 reply_queue->impl_.get());
523}
524
525void TaskQueue::PostTaskAndReply(std::unique_ptr<QueuedTask> task,
526 std::unique_ptr<QueuedTask> reply) {
527 return TaskQueue::impl_->PostTaskAndReply(std::move(task), std::move(reply),
528 impl_.get());
529}
530
531void TaskQueue::PostDelayedTask(std::unique_ptr<QueuedTask> task,
532 uint32_t milliseconds) {
533 return TaskQueue::impl_->PostDelayedTask(std::move(task), milliseconds);
534}
535
tommic06b1332016-05-14 18:31:40536} // namespace rtc