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_gcd.h"
16 
17 #include <dispatch/dispatch.h>
18 #include <string.h>
19 
20 #include <memory>
21 
22 #include "absl/strings/string_view.h"
23 #include "api/task_queue/queued_task.h"
24 #include "api/task_queue/task_queue_base.h"
25 #include "rtc_base/checks.h"
26 #include "rtc_base/logging.h"
27 #include "rtc_base/system/gcd_helpers.h"
28 
29 namespace webrtc {
30 namespace {
31 
TaskQueuePriorityToGCD(TaskQueueFactory::Priority priority)32 int TaskQueuePriorityToGCD(TaskQueueFactory::Priority priority) {
33   switch (priority) {
34     case TaskQueueFactory::Priority::NORMAL:
35       return DISPATCH_QUEUE_PRIORITY_DEFAULT;
36     case TaskQueueFactory::Priority::HIGH:
37       return DISPATCH_QUEUE_PRIORITY_HIGH;
38     case TaskQueueFactory::Priority::LOW:
39       return DISPATCH_QUEUE_PRIORITY_LOW;
40   }
41 }
42 
43 class TaskQueueGcd : public TaskQueueBase {
44  public:
45   TaskQueueGcd(absl::string_view queue_name, int gcd_priority);
46 
47   void Delete() override;
48   void PostTask(std::unique_ptr<QueuedTask> task) override;
49   void PostDelayedTask(std::unique_ptr<QueuedTask> task,
50                        uint32_t milliseconds) override;
51 
52  private:
53   struct TaskContext {
TaskContextwebrtc::__anonea34392f0111::TaskQueueGcd::TaskContext54     TaskContext(TaskQueueGcd* queue, std::unique_ptr<QueuedTask> task)
55         : queue(queue), task(std::move(task)) {}
56 
57     TaskQueueGcd* const queue;
58     std::unique_ptr<QueuedTask> task;
59   };
60 
61   ~TaskQueueGcd() override;
62   static void RunTask(void* task_context);
63   static void SetNotActive(void* task_queue);
64   static void DeleteQueue(void* task_queue);
65 
66   dispatch_queue_t queue_;
67   bool is_active_;
68 };
69 
TaskQueueGcd(absl::string_view queue_name,int gcd_priority)70 TaskQueueGcd::TaskQueueGcd(absl::string_view queue_name, int gcd_priority)
71     : queue_(RTCDispatchQueueCreateWithTarget(
72           std::string(queue_name).c_str(),
73           DISPATCH_QUEUE_SERIAL,
74           dispatch_get_global_queue(gcd_priority, 0))),
75       is_active_(true) {
76   RTC_CHECK(queue_);
77   dispatch_set_context(queue_, this);
78   // Assign a finalizer that will delete the queue when the last reference
79   // is released. This may run after the TaskQueue::Delete.
80   dispatch_set_finalizer_f(queue_, &DeleteQueue);
81 }
82 
83 TaskQueueGcd::~TaskQueueGcd() = default;
84 
Delete()85 void TaskQueueGcd::Delete() {
86   RTC_DCHECK(!IsCurrent());
87   // Implementation/behavioral note:
88   // Dispatch queues are reference counted via calls to dispatch_retain and
89   // dispatch_release. Pending blocks submitted to a queue also hold a
90   // reference to the queue until they have finished. Once all references to a
91   // queue have been released, the queue will be deallocated by the system.
92   // This is why we check the is_active_ before running tasks.
93 
94   // Use dispatch_sync to set the is_active_ to guarantee that there's not a
95   // race with checking it from a task.
96   dispatch_sync_f(queue_, this, &SetNotActive);
97   dispatch_release(queue_);
98 }
99 
PostTask(std::unique_ptr<QueuedTask> task)100 void TaskQueueGcd::PostTask(std::unique_ptr<QueuedTask> task) {
101   auto* context = new TaskContext(this, std::move(task));
102   dispatch_async_f(queue_, context, &RunTask);
103 }
104 
PostDelayedTask(std::unique_ptr<QueuedTask> task,uint32_t milliseconds)105 void TaskQueueGcd::PostDelayedTask(std::unique_ptr<QueuedTask> task,
106                                    uint32_t milliseconds) {
107   auto* context = new TaskContext(this, std::move(task));
108   dispatch_after_f(
109       dispatch_time(DISPATCH_TIME_NOW, milliseconds * NSEC_PER_MSEC), queue_,
110       context, &RunTask);
111 }
112 
113 // static
RunTask(void * task_context)114 void TaskQueueGcd::RunTask(void* task_context) {
115   std::unique_ptr<TaskContext> tc(static_cast<TaskContext*>(task_context));
116   if (!tc->queue->is_active_)
117     return;
118 
119   CurrentTaskQueueSetter set_current(tc->queue);
120   auto* task = tc->task.release();
121   if (task->Run()) {
122     // Delete the task before CurrentTaskQueueSetter clears state that this code
123     // is running on the task queue.
124     delete task;
125   }
126 }
127 
128 // static
SetNotActive(void * task_queue)129 void TaskQueueGcd::SetNotActive(void* task_queue) {
130   static_cast<TaskQueueGcd*>(task_queue)->is_active_ = false;
131 }
132 
133 // static
DeleteQueue(void * task_queue)134 void TaskQueueGcd::DeleteQueue(void* task_queue) {
135   delete static_cast<TaskQueueGcd*>(task_queue);
136 }
137 
138 class TaskQueueGcdFactory final : public TaskQueueFactory {
139  public:
CreateTaskQueue(absl::string_view name,Priority priority) const140   std::unique_ptr<TaskQueueBase, TaskQueueDeleter> CreateTaskQueue(
141       absl::string_view name,
142       Priority priority) const override {
143     return std::unique_ptr<TaskQueueBase, TaskQueueDeleter>(
144         new TaskQueueGcd(name, TaskQueuePriorityToGCD(priority)));
145   }
146 };
147 
148 }  // namespace
149 
CreateTaskQueueGcdFactory()150 std::unique_ptr<TaskQueueFactory> CreateTaskQueueGcdFactory() {
151   return std::make_unique<TaskQueueGcdFactory>();
152 }
153 
154 }  // namespace webrtc
155