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