Stop using dispatch_set_target_queue()
[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_with_target(queue_name, DISPATCH_QUEUE_SERIAL, dispatch_get_global_queue(TaskQueuePriorityToGCD(priority), 0))),
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
153 TaskQueue::Impl::~Impl() {
154   RTC_DCHECK(!IsCurrent());
155   // Implementation/behavioral note:
156   // Dispatch queues are reference counted via calls to dispatch_retain and
157   // dispatch_release. Pending blocks submitted to a queue also hold a
158   // reference to the queue until they have finished. Once all references to a
159   // queue have been released, the queue will be deallocated by the system.
160   // This is why we check the context before running tasks.
161
162   // Use dispatch_sync to set the context to null to guarantee that there's not
163   // a race between checking the context and using it from a task.
164   dispatch_sync_f(queue_, context_, &QueueContext::SetNotActive);
165   dispatch_release(queue_);
166 }
167
168 // static
169 TaskQueue* TaskQueue::Impl::Current() {
170   return static_cast<TaskQueue*>(pthread_getspecific(GetQueuePtrTls()));
171 }
172
173 bool TaskQueue::Impl::IsCurrent() const {
174   RTC_DCHECK(queue_);
175   const TaskQueue* current = Current();
176   return current && this == current->impl_.get();
177 }
178
179 void TaskQueue::Impl::PostTask(std::unique_ptr<QueuedTask> task) {
180   auto* context = new TaskContext(context_, std::move(task));
181   dispatch_async_f(queue_, context, &TaskContext::RunTask);
182 }
183
184 void TaskQueue::Impl::PostDelayedTask(std::unique_ptr<QueuedTask> task,
185                                       uint32_t milliseconds) {
186   auto* context = new TaskContext(context_, std::move(task));
187   dispatch_after_f(
188       dispatch_time(DISPATCH_TIME_NOW, milliseconds * NSEC_PER_MSEC), queue_,
189       context, &TaskContext::RunTask);
190 }
191
192 void TaskQueue::Impl::PostTaskAndReply(std::unique_ptr<QueuedTask> task,
193                                        std::unique_ptr<QueuedTask> reply,
194                                        TaskQueue::Impl* reply_queue) {
195   auto* context = new PostTaskAndReplyContext(
196       context_, std::move(task), reply_queue->context_, std::move(reply));
197   dispatch_async_f(queue_, context, &PostTaskAndReplyContext::RunTask);
198 }
199
200 // Boilerplate for the PIMPL pattern.
201 TaskQueue::TaskQueue(const char* queue_name, Priority priority)
202     : impl_(new RefCountedObject<TaskQueue::Impl>(queue_name, this, priority)) {
203 }
204
205 TaskQueue::~TaskQueue() {}
206
207 // static
208 TaskQueue* TaskQueue::Current() {
209   return TaskQueue::Impl::Current();
210 }
211
212 // Used for DCHECKing the current queue.
213 bool TaskQueue::IsCurrent() const {
214   return impl_->IsCurrent();
215 }
216
217 void TaskQueue::PostTask(std::unique_ptr<QueuedTask> task) {
218   return TaskQueue::impl_->PostTask(std::move(task));
219 }
220
221 void TaskQueue::PostTaskAndReply(std::unique_ptr<QueuedTask> task,
222                                  std::unique_ptr<QueuedTask> reply,
223                                  TaskQueue* reply_queue) {
224   return TaskQueue::impl_->PostTaskAndReply(std::move(task), std::move(reply),
225                                             reply_queue->impl_.get());
226 }
227
228 void TaskQueue::PostTaskAndReply(std::unique_ptr<QueuedTask> task,
229                                  std::unique_ptr<QueuedTask> reply) {
230   return TaskQueue::impl_->PostTaskAndReply(std::move(task), std::move(reply),
231                                             impl_.get());
232 }
233
234 void TaskQueue::PostDelayedTask(std::unique_ptr<QueuedTask> task,
235                                 uint32_t milliseconds) {
236   return TaskQueue::impl_->PostDelayedTask(std::move(task), milliseconds);
237 }
238
239 }  // namespace rtc