blob: 35c7221e83fb17a3a5c9b40d76973040cabf1308 [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
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +010020#include "absl/memory/memory.h"
21#include "absl/strings/string_view.h"
22#include "api/task_queue/queued_task.h"
23#include "api/task_queue/task_queue_base.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020024#include "rtc_base/checks.h"
25#include "rtc_base/logging.h"
tommic06b1332016-05-14 11:31:40 -070026
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +010027namespace webrtc {
tommic9bb7912017-02-24 10:42:14 -080028namespace {
29
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +010030int TaskQueuePriorityToGCD(TaskQueueFactory::Priority priority) {
tommic9bb7912017-02-24 10:42:14 -080031 switch (priority) {
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +010032 case TaskQueueFactory::Priority::NORMAL:
tommic9bb7912017-02-24 10:42:14 -080033 return DISPATCH_QUEUE_PRIORITY_DEFAULT;
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +010034 case TaskQueueFactory::Priority::HIGH:
tommic9bb7912017-02-24 10:42:14 -080035 return DISPATCH_QUEUE_PRIORITY_HIGH;
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +010036 case TaskQueueFactory::Priority::LOW:
tommic9bb7912017-02-24 10:42:14 -080037 return DISPATCH_QUEUE_PRIORITY_LOW;
38 }
39}
tommic9bb7912017-02-24 10:42:14 -080040
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +010041class TaskQueueGcd : public TaskQueueBase {
nisse66d07c52017-09-08 05:12:51 -070042 public:
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +010043 TaskQueueGcd(absl::string_view queue_name, int gcd_priority);
tommic06b1332016-05-14 11:31:40 -070044
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +010045 void Delete() override;
46 void PostTask(std::unique_ptr<QueuedTask> task) override;
47 void PostDelayedTask(std::unique_ptr<QueuedTask> task,
48 uint32_t milliseconds) override;
tommic06b1332016-05-14 11:31:40 -070049
nisse66d07c52017-09-08 05:12:51 -070050 private:
nisse66d07c52017-09-08 05:12:51 -070051 struct TaskContext {
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +010052 TaskContext(TaskQueueGcd* queue, std::unique_ptr<QueuedTask> task)
53 : queue(queue), task(std::move(task)) {}
nisse66d07c52017-09-08 05:12:51 -070054
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +010055 TaskQueueGcd* const queue;
nisse66d07c52017-09-08 05:12:51 -070056 std::unique_ptr<QueuedTask> task;
57 };
58
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +010059 ~TaskQueueGcd() override;
60 static void RunTask(void* task_context);
61 static void SetNotActive(void* task_queue);
62 static void DeleteQueue(void* task_queue);
63
nisse66d07c52017-09-08 05:12:51 -070064 dispatch_queue_t queue_;
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +010065 bool is_active_;
tommic06b1332016-05-14 11:31:40 -070066};
67
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +010068TaskQueueGcd::TaskQueueGcd(absl::string_view queue_name, int gcd_priority)
69 : queue_(dispatch_queue_create(std::string(queue_name).c_str(),
70 DISPATCH_QUEUE_SERIAL)),
71 is_active_(true) {
tommic06b1332016-05-14 11:31:40 -070072 RTC_CHECK(queue_);
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +010073 dispatch_set_context(queue_, this);
74 // Assign a finalizer that will delete the queue when the last reference
75 // is released. This may run after the TaskQueue::Delete.
76 dispatch_set_finalizer_f(queue_, &DeleteQueue);
tommic9bb7912017-02-24 10:42:14 -080077
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +010078 dispatch_set_target_queue(queue_, dispatch_get_global_queue(gcd_priority, 0));
tommic06b1332016-05-14 11:31:40 -070079}
80
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +010081TaskQueueGcd::~TaskQueueGcd() = default;
82
83void TaskQueueGcd::Delete() {
tommic06b1332016-05-14 11:31:40 -070084 RTC_DCHECK(!IsCurrent());
85 // Implementation/behavioral note:
86 // Dispatch queues are reference counted via calls to dispatch_retain and
87 // dispatch_release. Pending blocks submitted to a queue also hold a
88 // reference to the queue until they have finished. Once all references to a
89 // queue have been released, the queue will be deallocated by the system.
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +010090 // This is why we check the is_active_ before running tasks.
tommic06b1332016-05-14 11:31:40 -070091
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +010092 // Use dispatch_sync to set the is_active_ to guarantee that there's not a
93 // race with checking it from a task.
94 dispatch_sync_f(queue_, this, &SetNotActive);
tommic06b1332016-05-14 11:31:40 -070095 dispatch_release(queue_);
96}
97
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +010098void TaskQueueGcd::PostTask(std::unique_ptr<QueuedTask> task) {
99 auto* context = new TaskContext(this, std::move(task));
100 dispatch_async_f(queue_, context, &RunTask);
tommic06b1332016-05-14 11:31:40 -0700101}
102
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +0100103void TaskQueueGcd::PostDelayedTask(std::unique_ptr<QueuedTask> task,
104 uint32_t milliseconds) {
105 auto* context = new TaskContext(this, std::move(task));
tommic06b1332016-05-14 11:31:40 -0700106 dispatch_after_f(
107 dispatch_time(DISPATCH_TIME_NOW, milliseconds * NSEC_PER_MSEC), queue_,
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +0100108 context, &RunTask);
tommic06b1332016-05-14 11:31:40 -0700109}
110
nisse66d07c52017-09-08 05:12:51 -0700111// static
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +0100112void TaskQueueGcd::RunTask(void* task_context) {
113 std::unique_ptr<TaskContext> tc(static_cast<TaskContext*>(task_context));
114 if (!tc->queue->is_active_)
115 return;
116
117 CurrentTaskQueueSetter set_current(tc->queue);
118 auto* task = tc->task.release();
119 if (task->Run()) {
120 // Delete the task before CurrentTaskQueueSetter clears state that this code
121 // is running on the task queue.
122 delete task;
123 }
nisse66d07c52017-09-08 05:12:51 -0700124}
125
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +0100126// static
127void TaskQueueGcd::SetNotActive(void* task_queue) {
128 static_cast<TaskQueueGcd*>(task_queue)->is_active_ = false;
nisse66d07c52017-09-08 05:12:51 -0700129}
130
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +0100131// static
132void TaskQueueGcd::DeleteQueue(void* task_queue) {
133 delete static_cast<TaskQueueGcd*>(task_queue);
nisse66d07c52017-09-08 05:12:51 -0700134}
135
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +0100136class TaskQueueGcdFactory final : public TaskQueueFactory {
137 public:
138 std::unique_ptr<TaskQueueBase, TaskQueueDeleter> CreateTaskQueue(
139 absl::string_view name,
140 Priority priority) const override {
141 return std::unique_ptr<TaskQueueBase, TaskQueueDeleter>(
142 new TaskQueueGcd(name, TaskQueuePriorityToGCD(priority)));
143 }
144};
145
146} // namespace
147
148std::unique_ptr<TaskQueueFactory> CreateTaskQueueGcdFactory() {
149 return absl::make_unique<TaskQueueGcdFactory>();
tommic06b1332016-05-14 11:31:40 -0700150}
151
Danil Chapovalov47cf5ea2019-02-19 20:20:16 +0100152} // namespace webrtc