xref: /aosp_15_r20/external/webrtc/rtc_base/task_queue_gcd.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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