blob: 144cc765d6e9d245244cb4aab186f8314717b4a6 [file] [log] [blame] [edit]
// Copyright 2018 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/fit/scheduler.h>
#include <map>
#include <queue>
#include <utility>
namespace fit {
namespace subtle {
scheduler::scheduler() = default;
scheduler::~scheduler() = default;
void scheduler::schedule_task(pending_task task) {
assert(task);
runnable_tasks_.push(std::move(task));
}
suspended_task::ticket scheduler::obtain_ticket(uint32_t initial_refs) {
suspended_task::ticket ticket = next_ticket_++;
tickets_.emplace(ticket, ticket_record(initial_refs));
return ticket;
}
void scheduler::finalize_ticket(suspended_task::ticket ticket, pending_task* task) {
auto it = tickets_.find(ticket);
assert(it != tickets_.end());
assert(!it->second.task);
assert(it->second.ref_count > 0);
assert(task);
it->second.ref_count--;
if (!*task) {
// task already finished
} else if (it->second.was_resumed) {
// task immediately became runnable
runnable_tasks_.push(std::move(*task));
} else if (it->second.ref_count > 0) {
// task remains suspended
it->second.task = std::move(*task);
suspended_task_count_++;
} // else, task was abandoned and caller retains ownership of it
if (it->second.ref_count == 0) {
tickets_.erase(it);
}
}
void scheduler::duplicate_ticket(suspended_task::ticket ticket) {
auto it = tickets_.find(ticket);
assert(it != tickets_.end());
assert(it->second.ref_count > 0);
it->second.ref_count++;
assert(it->second.ref_count != 0); // did we really make 4 billion refs?!
}
pending_task scheduler::release_ticket(suspended_task::ticket ticket) {
auto it = tickets_.find(ticket);
assert(it != tickets_.end());
assert(it->second.ref_count > 0);
it->second.ref_count--;
if (it->second.ref_count == 0) {
pending_task task = std::move(it->second.task);
if (task) {
assert(suspended_task_count_ > 0);
suspended_task_count_--;
}
tickets_.erase(it);
return task;
}
return pending_task();
}
bool scheduler::resume_task_with_ticket(suspended_task::ticket ticket) {
auto it = tickets_.find(ticket);
assert(it != tickets_.end());
assert(it->second.ref_count > 0);
bool did_resume = false;
it->second.ref_count--;
if (!it->second.was_resumed) {
it->second.was_resumed = true;
if (it->second.task) {
did_resume = true;
assert(suspended_task_count_ > 0);
suspended_task_count_--;
runnable_tasks_.push(std::move(it->second.task));
}
}
if (it->second.ref_count == 0) {
tickets_.erase(it);
}
return did_resume;
}
void scheduler::take_runnable_tasks(task_queue* tasks) {
assert(tasks && tasks->empty());
runnable_tasks_.swap(*tasks);
}
void scheduler::take_all_tasks(task_queue* tasks) {
assert(tasks && tasks->empty());
runnable_tasks_.swap(*tasks);
if (suspended_task_count_ > 0) {
for (auto& item : tickets_) {
if (item.second.task) {
assert(suspended_task_count_ > 0);
suspended_task_count_--;
tasks->push(std::move(item.second.task));
}
}
}
}
} // namespace subtle
} // namespace fit