You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
123 lines
3.1 KiB
123 lines
3.1 KiB
// 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
|