blob: e498ba30175a64a7ab2a190cb0d29c48ca7fe2e4 [file] [log] [blame]
tommic06b1332016-05-14 11:31:40 -07001/*
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
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +010015#include "rtc_base/task_queue_gcd.h"
tommic06b1332016-05-14 11:31:40 -070016
nisse66d07c52017-09-08 05:12:51 -070017#include <dispatch/dispatch.h>
Jonas Olssona4d87372019-07-05 19:08:33 +020018#include <string.h>
nisse66d07c52017-09-08 05:12:51 -070019
Mirko Bonadei317a1f02019-09-17 17:06:18 +020020#include <memory>
21
Danil Chapovalovfcfa80f2022-07-07 11:26:48 +020022#include "absl/functional/any_invocable.h"
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +010023#include "absl/strings/string_view.h"
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +010024#include "api/task_queue/task_queue_base.h"
Danil Chapovalovfcfa80f2022-07-07 11:26:48 +020025#include "api/units/time_delta.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020026#include "rtc_base/checks.h"
27#include "rtc_base/logging.h"
Yura Yaroshevichebf739b2020-03-16 18:00:59 +030028#include "rtc_base/system/gcd_helpers.h"
tommic06b1332016-05-14 11:31:40 -070029
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +010030namespace webrtc {
tommic9bb7912017-02-24 10:42:14 -080031namespace {
32
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +010033int TaskQueuePriorityToGCD(TaskQueueFactory::Priority priority) {
tommic9bb7912017-02-24 10:42:14 -080034 switch (priority) {
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +010035 case TaskQueueFactory::Priority::NORMAL:
tommic9bb7912017-02-24 10:42:14 -080036 return DISPATCH_QUEUE_PRIORITY_DEFAULT;
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +010037 case TaskQueueFactory::Priority::HIGH:
tommic9bb7912017-02-24 10:42:14 -080038 return DISPATCH_QUEUE_PRIORITY_HIGH;
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +010039 case TaskQueueFactory::Priority::LOW:
tommic9bb7912017-02-24 10:42:14 -080040 return DISPATCH_QUEUE_PRIORITY_LOW;
41 }
42}
tommic9bb7912017-02-24 10:42:14 -080043
Danil Chapovalovfcfa80f2022-07-07 11:26:48 +020044class TaskQueueGcd final : public TaskQueueBase {
nisse66d07c52017-09-08 05:12:51 -070045 public:
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +010046 TaskQueueGcd(absl::string_view queue_name, int gcd_priority);
tommic06b1332016-05-14 11:31:40 -070047
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +010048 void Delete() override;
Danil Chapovalovfcfa80f2022-07-07 11:26:48 +020049 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;
tommic06b1332016-05-14 11:31:40 -070054
nisse66d07c52017-09-08 05:12:51 -070055 private:
nisse66d07c52017-09-08 05:12:51 -070056 struct TaskContext {
Danil Chapovalovfcfa80f2022-07-07 11:26:48 +020057 TaskContext(TaskQueueGcd* queue, absl::AnyInvocable<void() &&> task)
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +010058 : queue(queue), task(std::move(task)) {}
nisse66d07c52017-09-08 05:12:51 -070059
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +010060 TaskQueueGcd* const queue;
Danil Chapovalovfcfa80f2022-07-07 11:26:48 +020061 absl::AnyInvocable<void() &&> task;
nisse66d07c52017-09-08 05:12:51 -070062 };
63
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +010064 ~TaskQueueGcd() override;
65 static void RunTask(void* task_context);
66 static void SetNotActive(void* task_queue);
67 static void DeleteQueue(void* task_queue);
68
nisse66d07c52017-09-08 05:12:51 -070069 dispatch_queue_t queue_;
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +010070 bool is_active_;
tommic06b1332016-05-14 11:31:40 -070071};
72
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +010073TaskQueueGcd::TaskQueueGcd(absl::string_view queue_name, int gcd_priority)
Yura Yaroshevichebf739b2020-03-16 18:00:59 +030074 : queue_(RTCDispatchQueueCreateWithTarget(
75 std::string(queue_name).c_str(),
76 DISPATCH_QUEUE_SERIAL,
77 dispatch_get_global_queue(gcd_priority, 0))),
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +010078 is_active_(true) {
tommic06b1332016-05-14 11:31:40 -070079 RTC_CHECK(queue_);
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +010080 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);
tommic06b1332016-05-14 11:31:40 -070084}
85
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +010086TaskQueueGcd::~TaskQueueGcd() = default;
87
88void TaskQueueGcd::Delete() {
tommic06b1332016-05-14 11:31:40 -070089 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.
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +010095 // This is why we check the is_active_ before running tasks.
tommic06b1332016-05-14 11:31:40 -070096
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +010097 // 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);
tommic06b1332016-05-14 11:31:40 -0700100 dispatch_release(queue_);
101}
102
Danil Chapovalovfcfa80f2022-07-07 11:26:48 +0200103void TaskQueueGcd::PostTask(absl::AnyInvocable<void() &&> task) {
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +0100104 auto* context = new TaskContext(this, std::move(task));
105 dispatch_async_f(queue_, context, &RunTask);
tommic06b1332016-05-14 11:31:40 -0700106}
107
Danil Chapovalovfcfa80f2022-07-07 11:26:48 +0200108void TaskQueueGcd::PostDelayedTask(absl::AnyInvocable<void() &&> task,
109 TimeDelta delay) {
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +0100110 auto* context = new TaskContext(this, std::move(task));
Danil Chapovalovfcfa80f2022-07-07 11:26:48 +0200111 dispatch_after_f(dispatch_time(DISPATCH_TIME_NOW, delay.us() * NSEC_PER_USEC),
112 queue_, context, &RunTask);
113}
114
115void TaskQueueGcd::PostDelayedHighPrecisionTask(
116 absl::AnyInvocable<void() &&> task,
117 TimeDelta delay) {
118 PostDelayedTask(std::move(task), delay);
tommic06b1332016-05-14 11:31:40 -0700119}
120
nisse66d07c52017-09-08 05:12:51 -0700121// static
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +0100122void 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);
Danil Chapovalovfcfa80f2022-07-07 11:26:48 +0200128 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;
nisse66d07c52017-09-08 05:12:51 -0700132}
133
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +0100134// static
135void TaskQueueGcd::SetNotActive(void* task_queue) {
136 static_cast<TaskQueueGcd*>(task_queue)->is_active_ = false;
nisse66d07c52017-09-08 05:12:51 -0700137}
138
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +0100139// static
140void TaskQueueGcd::DeleteQueue(void* task_queue) {
141 delete static_cast<TaskQueueGcd*>(task_queue);
nisse66d07c52017-09-08 05:12:51 -0700142}
143
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +0100144class TaskQueueGcdFactory final : public TaskQueueFactory {
145 public:
146 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
156std::unique_ptr<TaskQueueFactory> CreateTaskQueueGcdFactory() {
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200157 return std::make_unique<TaskQueueGcdFactory>();
tommic06b1332016-05-14 11:31:40 -0700158}
159
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +0100160} // namespace webrtc