a13e088ede60bd91d6649121bfa134634c4af48d
[WebKit-https.git] / Source / ThirdParty / libwebrtc / Source / webrtc / rtc_base / task_queue_gcd.cc
1 /*
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
11 // This file contains the implementation of TaskQueue for Mac and iOS.
12 // The implementation uses Grand Central Dispatch queues (GCD) to
13 // do the actual task queuing.
14
15 #include "rtc_base/task_queue.h"
16
17 #include <string.h>
18
19 #include <dispatch/dispatch.h>
20
21 #include "rtc_base/checks.h"
22 #include "rtc_base/logging.h"
23 #include "rtc_base/refcount.h"
24 #include "rtc_base/refcountedobject.h"
25 #include "rtc_base/task_queue_posix.h"
26
27 namespace rtc {
28 namespace {
29
30 using Priority = TaskQueue::Priority;
31
32 int TaskQueuePriorityToGCD(Priority priority) {
33   switch (priority) {
34     case Priority::NORMAL:
35       return DISPATCH_QUEUE_PRIORITY_DEFAULT;
36     case Priority::HIGH:
37       return DISPATCH_QUEUE_PRIORITY_HIGH;
38     case Priority::LOW:
39       return DISPATCH_QUEUE_PRIORITY_LOW;
40   }
41 }
42 }
43
44 using internal::GetQueuePtrTls;
45 using internal::AutoSetCurrentQueuePtr;
46
47 class TaskQueue::Impl : public RefCountInterface {
48  public:
49   Impl(const char* queue_name, TaskQueue* task_queue, Priority priority);
50   ~Impl() override;
51
52   static TaskQueue* Current();
53
54   // Used for DCHECKing the current queue.
55   bool IsCurrent() const;
56
57   void PostTask(std::unique_ptr<QueuedTask> task);
58   void PostTaskAndReply(std::unique_ptr<QueuedTask> task,
59                         std::unique_ptr<QueuedTask> reply,
60                         TaskQueue::Impl* reply_queue);
61
62   void PostDelayedTask(std::unique_ptr<QueuedTask> task, uint32_t milliseconds);
63
64  private:
65   struct QueueContext {
66     explicit QueueContext(TaskQueue* q) : queue(q), is_active(true) {}
67
68     static void SetNotActive(void* context) {
69       QueueContext* qc = static_cast<QueueContext*>(context);
70       qc->is_active = false;
71     }
72
73     static void DeleteContext(void* context) {
74       QueueContext* qc = static_cast<QueueContext*>(context);
75       delete qc;
76     }
77
78     TaskQueue* const queue;
79     bool is_active;
80   };
81
82   struct TaskContext {
83     TaskContext(QueueContext* queue_ctx, std::unique_ptr<QueuedTask> task)
84         : queue_ctx(queue_ctx), task(std::move(task)) {}
85     virtual ~TaskContext() {}
86
87     static void RunTask(void* context) {
88       std::unique_ptr<TaskContext> tc(static_cast<TaskContext*>(context));
89       if (tc->queue_ctx->is_active) {
90         AutoSetCurrentQueuePtr set_current(tc->queue_ctx->queue);
91         if (!tc->task->Run())
92           tc->task.release();
93       }
94     }
95
96     QueueContext* const queue_ctx;
97     std::unique_ptr<QueuedTask> task;
98   };
99
100   // Special case context for holding two tasks, a |first_task| + the task
101   // that's owned by the parent struct, TaskContext, that then becomes the
102   // second (i.e. 'reply') task.
103   struct PostTaskAndReplyContext : public TaskContext {
104     explicit PostTaskAndReplyContext(QueueContext* first_queue_ctx,
105                                      std::unique_ptr<QueuedTask> first_task,
106                                      QueueContext* second_queue_ctx,
107                                      std::unique_ptr<QueuedTask> second_task)
108         : TaskContext(second_queue_ctx, std::move(second_task)),
109           first_queue_ctx(first_queue_ctx),
110           first_task(std::move(first_task)),
111           reply_queue_(second_queue_ctx->queue->impl_->queue_) {
112       // Retain the reply queue for as long as this object lives.
113       // If we don't, we may have memory leaks and/or failures.
114       dispatch_retain(reply_queue_);
115     }
116     ~PostTaskAndReplyContext() override { dispatch_release(reply_queue_); }
117
118     static void RunTask(void* context) {
119       auto* rc = static_cast<PostTaskAndReplyContext*>(context);
120       if (rc->first_queue_ctx->is_active) {
121         AutoSetCurrentQueuePtr set_current(rc->first_queue_ctx->queue);
122         if (!rc->first_task->Run())
123           rc->first_task.release();
124       }
125       // Post the reply task.  This hands the work over to the parent struct.
126       // This task will eventually delete |this|.
127       dispatch_async_f(rc->reply_queue_, rc, &TaskContext::RunTask);
128     }
129
130     QueueContext* const first_queue_ctx;
131     std::unique_ptr<QueuedTask> first_task;
132     dispatch_queue_t reply_queue_;
133   };
134
135   dispatch_queue_t queue_;
136   QueueContext* const context_;
137 };
138
139 TaskQueue::Impl::Impl(const char* queue_name,
140                       TaskQueue* task_queue,
141                       Priority priority)
142     : queue_(dispatch_queue_create(queue_name, DISPATCH_QUEUE_SERIAL)),
143       context_(new QueueContext(task_queue)) {
144   RTC_DCHECK(queue_name);
145   RTC_CHECK(queue_);
146   dispatch_set_context(queue_, context_);
147   // Assign a finalizer that will delete the context when the last reference
148   // to the queue is released.  This may run after the TaskQueue object has
149   // been deleted.
150   dispatch_set_finalizer_f(queue_, &QueueContext::DeleteContext);
151
152   dispatch_set_target_queue(
153       queue_, dispatch_get_global_queue(TaskQueuePriorityToGCD(priority), 0));
154 }
155
156 TaskQueue::Impl::~Impl() {
157   RTC_DCHECK(!IsCurrent());
158   // Implementation/behavioral note:
159   // Dispatch queues are reference counted via calls to dispatch_retain and
160   // dispatch_release. Pending blocks submitted to a queue also hold a
161   // reference to the queue until they have finished. Once all references to a
162   // queue have been released, the queue will be deallocated by the system.
163   // This is why we check the context before running tasks.
164
165   // Use dispatch_sync to set the context to null to guarantee that there's not
166   // a race between checking the context and using it from a task.
167   dispatch_sync_f(queue_, context_, &QueueContext::SetNotActive);
168   dispatch_release(queue_);
169 }
170
171 // static
172 TaskQueue* TaskQueue::Impl::Current() {
173   return static_cast<TaskQueue*>(pthread_getspecific(GetQueuePtrTls()));
174 }
175
176 bool TaskQueue::Impl::IsCurrent() const {
177   RTC_DCHECK(queue_);
178   const TaskQueue* current = Current();
179   return current && this == current->impl_.get();
180 }
181
182 void TaskQueue::Impl::PostTask(std::unique_ptr<QueuedTask> task) {
183   auto* context = new TaskContext(context_, std::move(task));
184   dispatch_async_f(queue_, context, &TaskContext::RunTask);
185 }
186
187 void TaskQueue::Impl::PostDelayedTask(std::unique_ptr<QueuedTask> task,
188                                       uint32_t milliseconds) {
189   auto* context = new TaskContext(context_, std::move(task));
190   dispatch_after_f(
191       dispatch_time(DISPATCH_TIME_NOW, milliseconds * NSEC_PER_MSEC), queue_,
192       context, &TaskContext::RunTask);
193 }
194
195 void TaskQueue::Impl::PostTaskAndReply(std::unique_ptr<QueuedTask> task,
196                                        std::unique_ptr<QueuedTask> reply,
197                                        TaskQueue::Impl* reply_queue) {
198   auto* context = new PostTaskAndReplyContext(
199       context_, std::move(task), reply_queue->context_, std::move(reply));
200   dispatch_async_f(queue_, context, &PostTaskAndReplyContext::RunTask);
201 }
202
203 // Boilerplate for the PIMPL pattern.
204 TaskQueue::TaskQueue(const char* queue_name, Priority priority)
205     : impl_(new RefCountedObject<TaskQueue::Impl>(queue_name, this, priority)) {
206 }
207
208 TaskQueue::~TaskQueue() {}
209
210 // static
211 TaskQueue* TaskQueue::Current() {
212   return TaskQueue::Impl::Current();
213 }
214
215 // Used for DCHECKing the current queue.
216 bool TaskQueue::IsCurrent() const {
217   return impl_->IsCurrent();
218 }
219
220 void TaskQueue::PostTask(std::unique_ptr<QueuedTask> task) {
221   return TaskQueue::impl_->PostTask(std::move(task));
222 }
223
224 void TaskQueue::PostTaskAndReply(std::unique_ptr<QueuedTask> task,
225                                  std::unique_ptr<QueuedTask> reply,
226                                  TaskQueue* reply_queue) {
227   return TaskQueue::impl_->PostTaskAndReply(std::move(task), std::move(reply),
228                                             reply_queue->impl_.get());
229 }
230
231 void TaskQueue::PostTaskAndReply(std::unique_ptr<QueuedTask> task,
232                                  std::unique_ptr<QueuedTask> reply) {
233   return TaskQueue::impl_->PostTaskAndReply(std::move(task), std::move(reply),
234                                             impl_.get());
235 }
236
237 void TaskQueue::PostDelayedTask(std::unique_ptr<QueuedTask> task,
238                                 uint32_t milliseconds) {
239   return TaskQueue::impl_->PostDelayedTask(std::move(task), milliseconds);
240 }
241
242 }  // namespace rtc