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