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