rust/src/rt/rust_scheduler.cpp
Brian Anderson 028af5cb6c rt: Change the lifecycle of tasks and schedulers for various reasons
This is in preparation for giving schedulers their own life cycle separate
from the kernel.

Tasks must be deleted before their scheduler thread, so we can't let the
scheduler exit before all its tasks have been cleaned up. In this scheme,
the scheduler will unregister tasks with the kernel when they are reaped,
then drop their ref on the task (there may still be others). When the task
ref count hits zero, the task will request to be unregistered from the
scheduler, which is responsible for deleting the task.

Instead of having the kernel tell the scheduler to exit, let the scheduler
decide when to exit. For now it will exit when all of its tasks are
unregistered.
2012-02-08 15:42:51 -08:00

141 lines
3.4 KiB
C++

#include "rust_scheduler.h"
#include "rust_util.h"
rust_scheduler::rust_scheduler(rust_kernel *kernel,
rust_srv *srv,
size_t num_threads,
rust_sched_id id) :
kernel(kernel),
srv(srv),
env(srv->env),
live_threads(num_threads),
live_tasks(0),
num_threads(num_threads),
id(id)
{
isaac_init(kernel, &rctx);
create_task_threads();
}
rust_scheduler::~rust_scheduler() {
destroy_task_threads();
}
rust_task_thread *
rust_scheduler::create_task_thread(int id) {
rust_srv *srv = this->srv->clone();
rust_task_thread *thread =
new (kernel, "rust_task_thread") rust_task_thread(this, srv, id);
KLOG(kernel, kern, "created task thread: " PTR ", id: %d, index: %d",
thread, id, thread->list_index);
return thread;
}
void
rust_scheduler::destroy_task_thread(rust_task_thread *thread) {
KLOG(kernel, kern, "deleting task thread: " PTR ", name: %s, index: %d",
thread, thread->name, thread->list_index);
rust_srv *srv = thread->srv;
delete thread;
delete srv;
}
void
rust_scheduler::create_task_threads() {
KLOG(kernel, kern, "Using %d scheduler threads.", num_threads);
for(size_t i = 0; i < num_threads; ++i) {
threads.push(create_task_thread(i));
}
}
void
rust_scheduler::destroy_task_threads() {
for(size_t i = 0; i < num_threads; ++i) {
destroy_task_thread(threads[i]);
}
}
void
rust_scheduler::start_task_threads()
{
// Copy num_threads because it's possible for the last thread
// to terminate and have the kernel delete us before we
// hit the last check against num_threads, in which case
// we would be accessing invalid memory.
uintptr_t num_threads = this->num_threads;
for(size_t i = 0; i < num_threads; ++i) {
rust_task_thread *thread = threads[i];
thread->start();
}
}
void
rust_scheduler::kill_all_tasks() {
for(size_t i = 0; i < num_threads; ++i) {
rust_task_thread *thread = threads[i];
thread->kill_all_tasks();
}
}
rust_task_id
rust_scheduler::create_task(rust_task *spawner, const char *name,
size_t init_stack_sz) {
size_t thread_no;
{
scoped_lock with(lock);
thread_no = isaac_rand(&rctx) % num_threads;
live_tasks++;
}
rust_task_thread *thread = threads[thread_no];
return thread->create_task(spawner, name, init_stack_sz);
}
rust_task_id
rust_scheduler::create_task(rust_task *spawner, const char *name) {
return create_task(spawner, name, env->min_stack_size);
}
void
rust_scheduler::release_task() {
bool need_exit = false;
{
scoped_lock with(lock);
live_tasks--;
if (live_tasks == 0) {
need_exit = true;
}
}
if (need_exit) {
// There are no more tasks on this scheduler. Time to leave
exit();
}
}
void
rust_scheduler::exit() {
// Take a copy of num_threads. After the last thread exits this
// scheduler will get destroyed, and our fields will cease to exist.
size_t current_num_threads = num_threads;
for(size_t i = 0; i < current_num_threads; ++i) {
threads[i]->exit();
}
}
size_t
rust_scheduler::number_of_threads() {
return num_threads;
}
void
rust_scheduler::release_task_thread() {
I(this, !lock.lock_held_by_current_thread());
uintptr_t new_live_threads;
{
scoped_lock with(lock);
new_live_threads = --live_threads;
}
if (new_live_threads == 0) {
kernel->release_scheduler_id(id);
}
}