// 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.
|
|
// Can't compile this for Zircon userspace yet since libstdc++ isn't available.
|
#ifndef FIT_NO_STD_FOR_ZIRCON_USERSPACE
|
|
#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
|
|
#endif // FIT_NO_STD_FOR_ZIRCON_USERSPACE
|