| // Copyright 2023 The Fuchsia Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include <lib/async/cpp/time.h> | |
| #include <lib/async/task.h> | |
| #include <lib/async_patterns/cpp/task_scope.h> | |
| #include <zircon/assert.h> | |
| #include <mutex> | |
| namespace async_patterns { | |
| namespace { | |
| constexpr char kTaskQueueThreadSafetyDescription[] = "|async_patterns::TaskQueue| is thread-unsafe"; | |
| } // namespace | |
| TaskScope::TaskScope(async_dispatcher_t* dispatcher) | |
| : dispatcher_(dispatcher), | |
| checker_(dispatcher, kTaskQueueThreadSafetyDescription), | |
| queue_(dispatcher, kTaskQueueThreadSafetyDescription) {} | |
| TaskScope::~TaskScope() { | |
| std::scoped_lock lock{checker_}; | |
| stopped_ = true; | |
| queue_.Stop(); | |
| list_node_t* node = nullptr; | |
| list_node_t* temp_node = nullptr; | |
| list_for_every_safe(&delayed_task_list_, node, temp_node) { | |
| auto* task = static_cast<DelayedTask*>(node); | |
| zx_status_t status = async_cancel_task(dispatcher_, task); | |
| ZX_DEBUG_ASSERT(status == ZX_OK); | |
| RemoveDelayedTaskLocked(task); | |
| } | |
| ZX_ASSERT(list_is_empty(&delayed_task_list_)); | |
| } | |
| TaskScope::DelayedTask::DelayedTask(TaskScope* owner) | |
| : list_node_t(LIST_INITIAL_CLEARED_VALUE), | |
| async_task_t{{ASYNC_STATE_INIT}, &DelayedTask::Handler, ZX_TIME_INFINITE_PAST}, | |
| owner_(owner) {} | |
| bool TaskScope::DelayedTask::Post(async_dispatcher_t* dispatcher, zx::time deadline) { | |
| async_task_t::deadline = deadline.get(); | |
| return async_post_task(dispatcher, this) == ZX_OK; | |
| } | |
| void TaskScope::DelayedTask::Handler(async_dispatcher_t* dispatcher, async_task_t* task, | |
| zx_status_t status) { | |
| auto* self = static_cast<DelayedTask*>(task); | |
| if (status == ZX_OK) { | |
| self->Run(); | |
| } | |
| self->owner_->RemoveDelayedTask(self); | |
| } | |
| void TaskScope::PostImpl(std::unique_ptr<internal::Task> task) { | |
| std::scoped_lock lock{checker_}; | |
| if (stopped_) { | |
| return; | |
| } | |
| queue_.Add(std::move(task)); | |
| } | |
| void TaskScope::PostImpl(std::unique_ptr<DelayedTask> task, zx::duration delay) { | |
| zx::time deadline = async::Now(dispatcher_) + delay; | |
| PostImpl(std::move(task), deadline); | |
| } | |
| void TaskScope::PostImpl(std::unique_ptr<DelayedTask> task, zx::time deadline) { | |
| std::scoped_lock lock{checker_}; | |
| if (stopped_) { | |
| return; | |
| } | |
| if (!task->Post(dispatcher_, deadline)) { | |
| return; | |
| } | |
| list_add_tail(&delayed_task_list_, task.release()); | |
| } | |
| void TaskScope::RemoveDelayedTask(DelayedTask* task) { | |
| std::scoped_lock lock{checker_}; | |
| RemoveDelayedTaskLocked(task); | |
| } | |
| // NOLINTNEXTLINE(readability-convert-member-functions-to-static): for thread safety annotations | |
| void TaskScope::RemoveDelayedTaskLocked(DelayedTask* task) { | |
| list_delete(task); | |
| delete task; | |
| } | |
| } // namespace async_patterns |