diff options
Diffstat (limited to 'third_party/libwebrtc/rtc_base/task_queue_gcd.cc')
-rw-r--r-- | third_party/libwebrtc/rtc_base/task_queue_gcd.cc | 160 |
1 files changed, 160 insertions, 0 deletions
diff --git a/third_party/libwebrtc/rtc_base/task_queue_gcd.cc b/third_party/libwebrtc/rtc_base/task_queue_gcd.cc new file mode 100644 index 0000000000..e498ba3017 --- /dev/null +++ b/third_party/libwebrtc/rtc_base/task_queue_gcd.cc @@ -0,0 +1,160 @@ +/* + * Copyright 2016 The WebRTC Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +// This file contains the implementation of TaskQueue for Mac and iOS. +// The implementation uses Grand Central Dispatch queues (GCD) to +// do the actual task queuing. + +#include "rtc_base/task_queue_gcd.h" + +#include <dispatch/dispatch.h> +#include <string.h> + +#include <memory> + +#include "absl/functional/any_invocable.h" +#include "absl/strings/string_view.h" +#include "api/task_queue/task_queue_base.h" +#include "api/units/time_delta.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/system/gcd_helpers.h" + +namespace webrtc { +namespace { + +int TaskQueuePriorityToGCD(TaskQueueFactory::Priority priority) { + switch (priority) { + case TaskQueueFactory::Priority::NORMAL: + return DISPATCH_QUEUE_PRIORITY_DEFAULT; + case TaskQueueFactory::Priority::HIGH: + return DISPATCH_QUEUE_PRIORITY_HIGH; + case TaskQueueFactory::Priority::LOW: + return DISPATCH_QUEUE_PRIORITY_LOW; + } +} + +class TaskQueueGcd final : public TaskQueueBase { + public: + TaskQueueGcd(absl::string_view queue_name, int gcd_priority); + + void Delete() override; + void PostTask(absl::AnyInvocable<void() &&> task) override; + void PostDelayedTask(absl::AnyInvocable<void() &&> task, + TimeDelta delay) override; + void PostDelayedHighPrecisionTask(absl::AnyInvocable<void() &&> task, + TimeDelta delay) override; + + private: + struct TaskContext { + TaskContext(TaskQueueGcd* queue, absl::AnyInvocable<void() &&> task) + : queue(queue), task(std::move(task)) {} + + TaskQueueGcd* const queue; + absl::AnyInvocable<void() &&> task; + }; + + ~TaskQueueGcd() override; + static void RunTask(void* task_context); + static void SetNotActive(void* task_queue); + static void DeleteQueue(void* task_queue); + + dispatch_queue_t queue_; + bool is_active_; +}; + +TaskQueueGcd::TaskQueueGcd(absl::string_view queue_name, int gcd_priority) + : queue_(RTCDispatchQueueCreateWithTarget( + std::string(queue_name).c_str(), + DISPATCH_QUEUE_SERIAL, + dispatch_get_global_queue(gcd_priority, 0))), + is_active_(true) { + RTC_CHECK(queue_); + dispatch_set_context(queue_, this); + // Assign a finalizer that will delete the queue when the last reference + // is released. This may run after the TaskQueue::Delete. + dispatch_set_finalizer_f(queue_, &DeleteQueue); +} + +TaskQueueGcd::~TaskQueueGcd() = default; + +void TaskQueueGcd::Delete() { + RTC_DCHECK(!IsCurrent()); + // Implementation/behavioral note: + // Dispatch queues are reference counted via calls to dispatch_retain and + // dispatch_release. Pending blocks submitted to a queue also hold a + // reference to the queue until they have finished. Once all references to a + // queue have been released, the queue will be deallocated by the system. + // This is why we check the is_active_ before running tasks. + + // Use dispatch_sync to set the is_active_ to guarantee that there's not a + // race with checking it from a task. + dispatch_sync_f(queue_, this, &SetNotActive); + dispatch_release(queue_); +} + +void TaskQueueGcd::PostTask(absl::AnyInvocable<void() &&> task) { + auto* context = new TaskContext(this, std::move(task)); + dispatch_async_f(queue_, context, &RunTask); +} + +void TaskQueueGcd::PostDelayedTask(absl::AnyInvocable<void() &&> task, + TimeDelta delay) { + auto* context = new TaskContext(this, std::move(task)); + dispatch_after_f(dispatch_time(DISPATCH_TIME_NOW, delay.us() * NSEC_PER_USEC), + queue_, context, &RunTask); +} + +void TaskQueueGcd::PostDelayedHighPrecisionTask( + absl::AnyInvocable<void() &&> task, + TimeDelta delay) { + PostDelayedTask(std::move(task), delay); +} + +// static +void TaskQueueGcd::RunTask(void* task_context) { + std::unique_ptr<TaskContext> tc(static_cast<TaskContext*>(task_context)); + if (!tc->queue->is_active_) + return; + + CurrentTaskQueueSetter set_current(tc->queue); + std::move(tc->task)(); + // Delete the task before CurrentTaskQueueSetter clears state that this code + // is running on the task queue. + tc = nullptr; +} + +// static +void TaskQueueGcd::SetNotActive(void* task_queue) { + static_cast<TaskQueueGcd*>(task_queue)->is_active_ = false; +} + +// static +void TaskQueueGcd::DeleteQueue(void* task_queue) { + delete static_cast<TaskQueueGcd*>(task_queue); +} + +class TaskQueueGcdFactory final : public TaskQueueFactory { + public: + std::unique_ptr<TaskQueueBase, TaskQueueDeleter> CreateTaskQueue( + absl::string_view name, + Priority priority) const override { + return std::unique_ptr<TaskQueueBase, TaskQueueDeleter>( + new TaskQueueGcd(name, TaskQueuePriorityToGCD(priority))); + } +}; + +} // namespace + +std::unique_ptr<TaskQueueFactory> CreateTaskQueueGcdFactory() { + return std::make_unique<TaskQueueGcdFactory>(); +} + +} // namespace webrtc |