blob: 2cf426b8baa688446d41a52a1cb9e586bb593a12 [file] [log] [blame]
// Copyright 2019 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 "task.h"
Task::Task(async_dispatcher_t* dispatcher, Completion completion, bool post_on_create)
: completion_(std::move(completion)), dispatcher_(dispatcher) {
if (post_on_create) {
ZX_ASSERT(async_task_.Post(dispatcher_) == ZX_OK);
}
}
Task::~Task() { ZX_ASSERT(dependents_.is_empty()); }
void Task::ExecuteTask(async_dispatcher_t* dispatcher, async::TaskBase* task, zx_status_t status) {
// If we've already completed, we have no more work to do.
// If we have outstanding dependencies, we'll be rescheduled when they're done
if (std::holds_alternative<zx_status_t>(status_) ||
finished_dependencies_count_ != total_dependencies_count_) {
return;
}
Run();
}
// Called to record a new dependency
void Task::AddDependency(const fbl::RefPtr<Task>& dependency) {
ZX_ASSERT_MSG(!dependency->is_completed(), "Tried adding already complete task %s as a dep of %s",
dependency->TaskDescription().c_str(), TaskDescription().c_str());
dependencies_.push_back(dependency.get());
dependency->self_ = dependency;
dependency->RegisterDependent(fbl::RefPtr(this));
}
void Task::RegisterDependent(fbl::RefPtr<Task> dependent) {
++dependent->total_dependencies_count_;
// Check if we're already completed
auto* status = std::get_if<zx_status_t>(&status_);
if (status != nullptr) {
return dependent->DependencyComplete(this, *status);
}
dependents_.push_back(std::move(dependent));
}
void Task::DependencyComplete(const Task* dependency, zx_status_t status) {
++finished_dependencies_count_;
// If this task is already scheduled to run, we shouldn't try to run it again.
if (!is_pending()) {
if (finished_dependencies_count_ == total_dependencies_count_) {
ZX_ASSERT(async_task_.Post(dispatcher_) == ZX_OK);
}
}
if (status != ZX_OK && std::holds_alternative<Incomplete>(status_)) {
DependencyFailed(status);
}
for (unsigned i = 0; i < dependencies_.size(); ++i) {
if (dependency == dependencies_[i]) {
dependencies_.erase(i);
return;
}
}
// The task may have been completed as part of |DependencyFailed|,
// in which case the list of dependencies would have already been cleared.
if (!is_completed()) {
ZX_PANIC("driver_manager: %s could not find dependency %s, already removed?\n",
TaskDescription().c_str(), dependency->TaskDescription().c_str());
}
}
void Task::Complete(zx_status_t status) {
ZX_ASSERT(std::holds_alternative<Incomplete>(status_));
status_ = status;
for (auto& dependent : dependents_) {
dependent->DependencyComplete(this, status);
}
dependents_.reset();
dependencies_.reset();
// Take an additional self reference while calling the completion, to prevent the completion from
// dropping the last reference and running the dtor.
fbl::RefPtr<Task> keep_alive(this);
Completion completion(std::move(completion_));
if (completion) {
completion(status);
}
self_.reset();
}