rust/src/rt/rust_kernel.cpp
Brian Anderson aee83d2ff1 rt: Only wake up all schedulers when no tasks are left
At the moment there's not really any reason to be raising this signal,
since they schedulers wake up periodically anyway, but once we remove
the timer this will be how the schedulers know to exit.
2012-02-02 18:10:24 -08:00

234 lines
5.5 KiB
C++

#include "rust_internal.h"
#include "rust_util.h"
#define KLOG_(...) \
KLOG(this, kern, __VA_ARGS__)
#define KLOG_ERR_(field, ...) \
KLOG_LVL(this, field, log_err, __VA_ARGS__)
rust_kernel::rust_kernel(rust_srv *srv, size_t num_threads) :
_region(srv, true),
_log(srv, NULL),
srv(srv),
max_id(0),
rval(0),
num_threads(num_threads),
live_tasks(0),
env(srv->env)
{
isaac_init(this, &rctx);
create_schedulers();
}
rust_scheduler *
rust_kernel::create_scheduler(int id) {
_kernel_lock.lock();
rust_srv *srv = this->srv->clone();
rust_scheduler *sched =
new (this, "rust_scheduler") rust_scheduler(this, srv, id);
KLOG_("created scheduler: " PTR ", id: %d, index: %d",
sched, id, sched->list_index);
_kernel_lock.unlock();
return sched;
}
void
rust_kernel::destroy_scheduler(rust_scheduler *sched) {
_kernel_lock.lock();
KLOG_("deleting scheduler: " PTR ", name: %s, index: %d",
sched, sched->name, sched->list_index);
rust_srv *srv = sched->srv;
delete sched;
delete srv;
_kernel_lock.unlock();
}
void rust_kernel::create_schedulers() {
KLOG_("Using %d scheduler threads.", num_threads);
for(size_t i = 0; i < num_threads; ++i) {
threads.push(create_scheduler(i));
}
}
void rust_kernel::destroy_schedulers() {
for(size_t i = 0; i < num_threads; ++i) {
destroy_scheduler(threads[i]);
}
}
void
rust_kernel::log_all_scheduler_state() {
for(size_t i = 0; i < num_threads; ++i) {
threads[i]->log_state();
}
}
/**
* Checks for simple deadlocks.
*/
bool
rust_kernel::is_deadlocked() {
return false;
}
void
rust_kernel::log(uint32_t level, char const *fmt, ...) {
char buf[BUF_BYTES];
va_list args;
va_start(args, fmt);
vsnprintf(buf, sizeof(buf), fmt, args);
_log.trace_ln(NULL, level, buf);
va_end(args);
}
void
rust_kernel::fatal(char const *fmt, ...) {
char buf[BUF_BYTES];
va_list args;
va_start(args, fmt);
vsnprintf(buf, sizeof(buf), fmt, args);
_log.trace_ln(NULL, (uint32_t)0, buf);
exit(1);
va_end(args);
}
rust_kernel::~rust_kernel() {
destroy_schedulers();
}
void *
rust_kernel::malloc(size_t size, const char *tag) {
return _region.malloc(size, tag);
}
void *
rust_kernel::realloc(void *mem, size_t size) {
return _region.realloc(mem, size);
}
void rust_kernel::free(void *mem) {
_region.free(mem);
}
void
rust_kernel::signal_kernel_lock() {
_kernel_lock.lock();
_kernel_lock.unlock();
}
int rust_kernel::start_task_threads()
{
for(size_t i = 0; i < num_threads; ++i) {
rust_scheduler *thread = threads[i];
thread->start();
}
for(size_t i = 0; i < num_threads; ++i) {
rust_scheduler *thread = threads[i];
thread->join();
}
return rval;
}
void
rust_kernel::fail() {
// FIXME: On windows we're getting "Application has requested the
// Runtime to terminate it in an unusual way" when trying to shutdown
// cleanly.
set_exit_status(PROC_FAIL_CODE);
#if defined(__WIN32__)
exit(rval);
#endif
for(size_t i = 0; i < num_threads; ++i) {
rust_scheduler *thread = threads[i];
thread->kill_all_tasks();
}
}
rust_task_id
rust_kernel::create_task(rust_task *spawner, const char *name,
size_t init_stack_sz) {
scoped_lock with(_kernel_lock);
rust_scheduler *thread = threads[isaac_rand(&rctx) % num_threads];
rust_task *t = thread->create_task(spawner, name, init_stack_sz);
t->user.id = max_id++;
task_table.put(t->user.id, t);
return t->user.id;
}
rust_task_id
rust_kernel::create_task(rust_task *spawner, const char *name) {
return create_task(spawner, name, env->min_stack_size);
}
rust_task *
rust_kernel::get_task_by_id(rust_task_id id) {
scoped_lock with(_kernel_lock);
rust_task *task = NULL;
// get leaves task unchanged if not found.
task_table.get(id, &task);
if(task) {
if(task->get_ref_count() == 0) {
// this means the destructor is running, since the destructor
// grabs the kernel lock to unregister the task. Pretend this
// doesn't actually exist.
return NULL;
}
else {
task->ref();
}
}
return task;
}
void
rust_kernel::release_task_id(rust_task_id id) {
scoped_lock with(_kernel_lock);
task_table.remove(id);
}
void rust_kernel::exit_schedulers() {
for(size_t i = 0; i < num_threads; ++i) {
threads[i]->exit();
}
}
#ifdef __WIN32__
void
rust_kernel::win32_require(LPCTSTR fn, BOOL ok) {
if (!ok) {
LPTSTR buf;
DWORD err = GetLastError();
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, err,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &buf, 0, NULL );
KLOG_ERR_(dom, "%s failed with error %ld: %s", fn, err, buf);
LocalFree((HLOCAL)buf);
I(this, ok);
}
}
#endif
void
rust_kernel::set_exit_status(int code) {
scoped_lock with(_kernel_lock);
// If we've already failed then that's the code we're going to use
if (rval != PROC_FAIL_CODE) {
rval = code;
}
}
//
// Local Variables:
// mode: C++
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
//