1
Fork 0

Cleanup, refactoring, and some runtime tests.

This commit is contained in:
Michael Bebenita 2010-09-10 01:21:29 -07:00
parent f985fded3e
commit a493350eb5
22 changed files with 279 additions and 129 deletions

View file

@ -257,6 +257,7 @@ RUNTIME_CS := rt/sync/timer.cpp \
rt/rust_comm.cpp \
rt/rust_dom.cpp \
rt/rust_task.cpp \
rt/rust_task_list.cpp \
rt/rust_proxy.cpp \
rt/rust_chan.cpp \
rt/rust_port.cpp \
@ -270,6 +271,7 @@ RUNTIME_CS := rt/sync/timer.cpp \
rt/rust_kernel.cpp \
rt/memory_region.cpp \
rt/test/rust_test_harness.cpp \
rt/test/rust_test_runtime.cpp \
rt/test/rust_test_util.cpp
RUNTIME_HDR := rt/globals.h \
@ -281,6 +283,7 @@ RUNTIME_HDR := rt/globals.h \
rt/rust_port.h \
rt/rust_dom.h \
rt/rust_task.h \
rt/rust_task_list.h \
rt/rust_proxy.h \
rt/rust_log.h \
rt/rust_message.h \
@ -297,6 +300,7 @@ RUNTIME_HDR := rt/globals.h \
rt/memory_region.h \
rt/memory.h \
rt/test/rust_test_harness.h \
rt/test/rust_test_runtime.h \
rt/test/rust_test_util.h
RUNTIME_INCS := -Irt/isaac -Irt/uthash

View file

@ -2,8 +2,6 @@
#include <stdarg.h>
#include "rust_internal.h"
template class ptr_vec<rust_task>;
rust_dom::rust_dom(rust_kernel *kernel,
rust_message_queue *message_queue, rust_srv *srv,
rust_crate const *root_crate, const char *name) :
@ -14,9 +12,10 @@ rust_dom::rust_dom(rust_kernel *kernel,
local_region(&srv->local_region),
synchronized_region(&srv->synchronized_region),
name(name),
running_tasks(this),
blocked_tasks(this),
dead_tasks(this),
newborn_tasks(this, "newborn"),
running_tasks(this, "running"),
blocked_tasks(this, "blocked"),
dead_tasks(this, "dead"),
caches(this),
root_task(NULL),
curr_task(NULL),
@ -31,28 +30,16 @@ rust_dom::rust_dom(rust_kernel *kernel,
pthread_attr_setstacksize(&attr, 1024 * 1024);
pthread_attr_setdetachstate(&attr, true);
#endif
root_task = new (this) rust_task(this, NULL, name);
}
static void
del_all_tasks(rust_dom *dom, ptr_vec<rust_task> *v) {
I(dom, v);
while (v->length()) {
dom->log(rust_log::TASK, "deleting task 0x%" PRIdPTR,
v->length() - 1);
delete v->pop();
}
root_task = create_task(NULL, name);
}
rust_dom::~rust_dom() {
log(rust_log::MEM | rust_log::DOM,
"~rust_dom %s @0x%" PRIxPTR, name, (uintptr_t)this);
log(rust_log::TASK, "deleting all running tasks");
del_all_tasks(this, &running_tasks);
log(rust_log::TASK, "deleting all blocked tasks");
del_all_tasks(this, &blocked_tasks);
log(rust_log::TASK, "deleting all dead tasks");
del_all_tasks(this, &dead_tasks);
newborn_tasks.delete_all();
running_tasks.delete_all();
blocked_tasks.delete_all();
dead_tasks.delete_all();
#ifndef __WIN32__
pthread_attr_destroy(&attr);
#endif
@ -198,42 +185,10 @@ rust_dom::win32_require(LPCTSTR fn, BOOL ok) {
#endif
size_t
rust_dom::n_live_tasks()
{
rust_dom::number_of_live_tasks() {
return running_tasks.length() + blocked_tasks.length();
}
void
rust_dom::add_task_to_state_vec(ptr_vec<rust_task> *v, rust_task *task)
{
log(rust_log::MEM|rust_log::TASK,
"adding task %s @0x%" PRIxPTR " in state '%s' to vec 0x%" PRIxPTR,
task->name, (uintptr_t)task, state_vec_name(v), (uintptr_t)v);
v->push(task);
}
void
rust_dom::remove_task_from_state_vec(ptr_vec<rust_task> *v, rust_task *task)
{
log(rust_log::MEM|rust_log::TASK,
"removing task %s @0x%" PRIxPTR " in state '%s' from vec 0x%" PRIxPTR,
task->name, (uintptr_t)task, state_vec_name(v), (uintptr_t)v);
I(this, (*v)[task->idx] == task);
v->swap_delete(task);
}
const char *
rust_dom::state_vec_name(ptr_vec<rust_task> *v)
{
if (v == &running_tasks)
return "running";
if (v == &blocked_tasks)
return "blocked";
I(this, v == &dead_tasks);
return "dead";
}
/**
* Delete any dead tasks.
*/
@ -243,7 +198,7 @@ rust_dom::reap_dead_tasks() {
rust_task *task = dead_tasks[i];
if (task->ref_count == 0) {
I(this, task->tasks_waiting_to_join.is_empty());
dead_tasks.swap_delete(task);
dead_tasks.remove(task);
log(rust_log::TASK,
"deleting unreferenced dead task %s @0x%" PRIxPTR,
task->name, task);
@ -288,7 +243,6 @@ rust_dom::schedule_task() {
return (rust_task *)running_tasks[i];
}
}
// log(rust_log::DOM|rust_log::TASK, "no schedulable tasks");
return NULL;
}
@ -334,16 +288,16 @@ rust_dom::log_state() {
* drop to zero.
*/
int
rust_dom::start_main_loop()
{
rust_dom::start_main_loop() {
// Make sure someone is watching, to pull us out of infinite loops.
rust_timer timer(this);
log(rust_log::DOM, "running main-loop on domain %s @0x%" PRIxPTR,
name, this);
logptr("exit-task glue", root_crate->get_exit_task_glue());
log(rust_log::DOM, "started domain loop");
log(rust_log::DOM | rust_log::MEM,
"activate glue: " PTR ", exit glue: " PTR,
root_crate->get_activate_glue(), root_crate->get_exit_task_glue());
while (n_live_tasks() > 0) {
while (number_of_live_tasks() > 0) {
A(this, kernel->is_deadlocked() == false, "deadlock");
drain_incoming_message_queue(true);
@ -377,7 +331,7 @@ rust_dom::start_main_loop()
(uintptr_t)scheduled_task,
scheduled_task->rust_sp,
scheduled_task->ref_count,
scheduled_task->state_str());
scheduled_task->state->name);
interrupt_flag = 0;
@ -388,7 +342,7 @@ rust_dom::start_main_loop()
" in state '%s', sp=0x%" PRIxPTR,
scheduled_task->name,
(uintptr_t)scheduled_task,
state_vec_name(scheduled_task->state),
scheduled_task->state->name,
scheduled_task->rust_sp);
I(this, scheduled_task->rust_sp >=
@ -443,6 +397,15 @@ rust_dom::get_cache(rust_crate const *crate) {
return cache;
}
rust_task *
rust_dom::create_task(rust_task *spawner, const char *name) {
rust_task *task =
new (this) rust_task (this, &newborn_tasks, spawner, name);
log(rust_log::TASK, "created task: " PTR ", spawner: %s, name: %s",
task, spawner ? spawner->name : "null", name);
newborn_tasks.append(task);
return task;
}
//
// Local Variables:

View file

@ -18,10 +18,14 @@ struct rust_dom : public kernel_owned<rust_dom>, rc_base<rust_dom>
memory_region local_region;
memory_region synchronized_region;
const char *const name;
ptr_vec<rust_task> running_tasks;
ptr_vec<rust_task> blocked_tasks;
ptr_vec<rust_task> dead_tasks;
rust_task_list newborn_tasks;
rust_task_list running_tasks;
rust_task_list blocked_tasks;
rust_task_list dead_tasks;
ptr_vec<rust_crate_cache> caches;
randctx rctx;
rust_task *root_task;
rust_task *curr_task;
@ -71,10 +75,7 @@ struct rust_dom : public kernel_owned<rust_dom>, rc_base<rust_dom>
#endif
rust_crate_cache *get_cache(rust_crate const *crate);
size_t n_live_tasks();
void add_task_to_state_vec(ptr_vec<rust_task> *v, rust_task *task);
void remove_task_from_state_vec(ptr_vec<rust_task> *v, rust_task *task);
const char *state_vec_name(ptr_vec<rust_task> *v);
size_t number_of_live_tasks();
void reap_dead_tasks();
rust_task *schedule_task();
@ -83,6 +84,8 @@ struct rust_dom : public kernel_owned<rust_dom>, rc_base<rust_dom>
void log_state();
static void log_all_state();
rust_task *create_task(rust_task *spawner, const char *name);
};
//

View file

@ -124,6 +124,8 @@ template <typename T> struct region_owned {
}
};
#include "rust_task_list.h"
// A cond(ition) is something we can block on. This can be a channel
// (writing), a port (reading) or a task (waiting).
@ -203,9 +205,7 @@ crate_rel(rust_crate const *crate, T const *t) {
typedef void CDECL (*activate_glue_ty)(rust_task *);
class
rust_crate
{
class rust_crate {
// The following fields are emitted by the compiler for the static
// rust_crate object inside each compiled crate.
@ -563,6 +563,7 @@ struct gc_alloc {
#include "test/rust_test_harness.h"
#include "test/rust_test_util.h"
#include "test/rust_test_runtime.h"
//
// Local Variables:

View file

@ -4,9 +4,7 @@ rust_kernel::rust_kernel(rust_srv *srv) :
_region(&srv->local_region),
_log(srv, NULL),
_srv(srv),
_interrupt_kernel_loop(FALSE),
domains(&srv->local_region),
message_queues(&srv->local_region) {
_interrupt_kernel_loop(FALSE) {
// Nop.
}
@ -22,6 +20,9 @@ rust_kernel::create_domain(const rust_crate *crate, const char *name) {
message_queue->associate(handle);
domains.append(dom);
message_queues.append(message_queue);
log(rust_log::KERN | rust_log::TASK,
"created domain: " PTR ", name: %s, index: %d, domains %d",
dom, name, dom->list_index, domains.length());
_kernel_lock.signal_all();
_kernel_lock.unlock();
return handle;
@ -30,8 +31,9 @@ rust_kernel::create_domain(const rust_crate *crate, const char *name) {
void
rust_kernel::destroy_domain(rust_dom *dom) {
_kernel_lock.lock();
log(rust_log::KERN, "deleting domain: " PTR ", index: %d, domains %d",
dom, dom->list_index, domains.length());
log(rust_log::KERN,
"deleting domain: " PTR ", name: %s, index: %d, domains %d",
dom, dom->name, dom->list_index, domains.length());
domains.remove(dom);
dom->message_queue->disassociate();
rust_srv *srv = dom->srv;

View file

@ -9,6 +9,7 @@
// FIXME (issue #151): This should be 0x300; the change here is for
// practicality's sake until stack growth is working.
static size_t const min_stk_bytes = 0x300000;
// static size_t const min_stk_bytes = 0x10000;
// Task stack segments. Heap allocated and chained together.
@ -54,7 +55,8 @@ align_down(uintptr_t sp)
}
rust_task::rust_task(rust_dom *dom, rust_task *spawner, const char *name) :
rust_task::rust_task(rust_dom *dom, rust_task_list *state,
rust_task *spawner, const char *name) :
maybe_proxy<rust_task>(this),
stk(new_stk(dom, 0)),
runtime_sp(0),
@ -63,11 +65,11 @@ rust_task::rust_task(rust_dom *dom, rust_task *spawner, const char *name) :
dom(dom),
cache(NULL),
name(name),
state(&dom->running_tasks),
state(state),
cond(NULL),
cond_name("none"),
supervisor(spawner),
idx(0),
list_index(-1),
rendezvous_ptr(0),
alarm(this),
handle(NULL)
@ -197,7 +199,7 @@ rust_task::start(uintptr_t exit_task_glue,
// Back up one, we overshot where sp should be.
rust_sp = (uintptr_t) (spp+1);
dom->add_task_to_state_vec(&dom->running_tasks, this);
transition(&dom->newborn_tasks, &dom->running_tasks);
}
void
@ -544,23 +546,14 @@ rust_task::free(void *p, bool is_gc)
}
}
const char *
rust_task::state_str() {
return dom->state_vec_name(state);
}
void
rust_task::transition(ptr_vec<rust_task> *src, ptr_vec<rust_task> *dst)
{
rust_task::transition(rust_task_list *src, rust_task_list *dst) {
I(dom, state == src);
dom->log(rust_log::TASK,
"task %s @0x%" PRIxPTR " state change '%s' -> '%s'",
name,
(uintptr_t)this,
dom->state_vec_name(src),
dom->state_vec_name(dst));
dom->remove_task_from_state_vec(src, this);
dom->add_task_to_state_vec(dst, this);
"task %s " PTR " state change '%s' -> '%s'",
name, (uintptr_t)this, src->name, dst->name);
src->remove(this);
dst->append(this);
state = dst;
}
@ -577,8 +570,7 @@ rust_task::block(rust_cond *on, const char* name) {
}
void
rust_task::wakeup(rust_cond *from)
{
rust_task::wakeup(rust_cond *from) {
A(dom, cond != NULL, "Cannot wake up unblocked task.");
log(rust_log::TASK, "Blocked on 0x%" PRIxPTR " woken up on 0x%" PRIxPTR,
(uintptr_t) cond, (uintptr_t) from);
@ -591,14 +583,12 @@ rust_task::wakeup(rust_cond *from)
}
void
rust_task::die()
{
rust_task::die() {
transition(&dom->running_tasks, &dom->dead_tasks);
}
void
rust_task::unblock()
{
rust_task::unblock() {
if (blocked())
wakeup(cond);
}

View file

@ -7,7 +7,6 @@
#include "util/array_list.h"
struct
rust_task : public maybe_proxy<rust_task>,
public dom_owned<rust_task>
@ -22,11 +21,11 @@ rust_task : public maybe_proxy<rust_task>,
// Fields known only to the runtime.
const char *const name;
ptr_vec<rust_task> *state;
rust_task_list *state;
rust_cond *cond;
const char *cond_name;
rust_task *supervisor; // Parent-link for failure propagation.
size_t idx;
int32_t list_index;
size_t gc_alloc_thresh;
size_t gc_alloc_accum;
@ -50,6 +49,7 @@ rust_task : public maybe_proxy<rust_task>,
// Only a pointer to 'name' is kept, so it must live as long as this task.
rust_task(rust_dom *dom,
rust_task_list *state,
rust_task *spawner,
const char *name);
@ -71,8 +71,7 @@ rust_task : public maybe_proxy<rust_task>,
void *realloc(void *data, size_t sz, bool gc_mem=false);
void free(void *p, bool gc_mem=false);
const char *state_str();
void transition(ptr_vec<rust_task> *svec, ptr_vec<rust_task> *dvec);
void transition(rust_task_list *src, rust_task_list *dst);
void block(rust_cond *on, const char* name);
void wakeup(rust_cond *from);

16
src/rt/rust_task_list.cpp Normal file
View file

@ -0,0 +1,16 @@
#include "rust_internal.h"
rust_task_list::rust_task_list (rust_dom *dom, const char* name) :
dom(dom), name(name) {
// Nop;
}
void
rust_task_list::delete_all() {
dom->log(rust_log::TASK, "deleting all %s tasks", name);
while (is_empty() == false) {
rust_task *task = pop_value();
dom->log(rust_log::TASK, "deleting task " PTR, task);
delete task;
}
}

16
src/rt/rust_task_list.h Normal file
View file

@ -0,0 +1,16 @@
#ifndef RUST_TASK_LIST_H
#define RUST_TASK_LIST_H
/**
* Used to indicate the state of a rust task.
*/
class rust_task_list : public indexed_list<rust_task>,
public dom_owned<rust_task_list> {
public:
rust_dom *dom;
const char* name;
rust_task_list (rust_dom *dom, const char* name);
void delete_all();
};
#endif /* RUST_TASK_LIST_H */

View file

@ -542,12 +542,8 @@ upcall_get_type_desc(rust_task *task,
extern "C" CDECL rust_task *
upcall_new_task(rust_task *spawner, const char *name) {
LOG_UPCALL_ENTRY(spawner);
rust_dom *dom = spawner->dom;
rust_task *task = new (dom) rust_task(dom, spawner, name);
dom->log(rust_log::UPCALL | rust_log::MEM | rust_log::TASK,
"upcall new_task(spawner %s @0x%" PRIxPTR ", %s) = 0x%" PRIxPTR,
spawner->name, spawner, name, task);
rust_task *task = dom->create_task(spawner, name);
return task;
}

View file

@ -11,6 +11,18 @@ void sync::yield() {
#endif
}
void sync::sleep(size_t timeout_in_ms) {
#ifdef __WIN32__
Sleep(timeout_in_ms);
#else
usleep(timeout_in_ms * 1000);
#endif
}
void sync::random_sleep(size_t max_timeout_in_ms) {
sleep(rand() % max_timeout_in_ms);
}
rust_thread::rust_thread() : _is_running(false), thread(0) {
// Nop.
}

View file

@ -4,6 +4,8 @@
class sync {
public:
static void yield();
static void sleep(size_t timeout_in_ms);
static void random_sleep(size_t max_timeout_in_ms);
template <class T>
static bool compare_and_swap(T *address,
T oldValue, T newValue) {

View file

@ -25,6 +25,11 @@ timer::get_elapsed_time() {
return get_time() - _start;
}
double
timer::get_elapsed_time_in_ms() {
return (double) get_elapsed_time() / 1000.0;
}
int64_t
timer::get_timeout() {
return _timeout - get_elapsed_time();

View file

@ -17,6 +17,7 @@ public:
timer();
void reset(uint64_t timeout);
uint64_t get_elapsed_time();
double get_elapsed_time_in_ms();
int64_t get_timeout();
bool has_timed_out();
virtual ~timer();

View file

@ -10,7 +10,9 @@ rust_test::name() {
return "untitled";
}
rust_test_suite::rust_test_suite() {
rust_test_suite::rust_test_suite(rust_crate *crate) : crate(crate) {
tests.append(new rust_domain_test());
tests.append(new rust_task_test(this));
tests.append(new rust_array_list_test());
tests.append(new rust_synchronized_indexed_list_test());
}
@ -25,11 +27,12 @@ rust_test_suite::run() {
for (size_t i = 0; i < tests.size(); i++) {
rust_test *test = tests[i];
printf("test: %s running ... \n", test->name());
if (tests[i]->run() == false) {
printf("test: %s FAILED\n", test->name());
timer timer;
bool result = tests[i]->run();
printf("test: %s %s %.2f ms\n", test->name(),
result ? "PASSED" : "FAILE", timer.get_elapsed_time_in_ms());
if (result == false) {
pass = false;
} else {
printf("test: %s PASSED\n", test->name());
}
}
return pass;

View file

@ -13,8 +13,9 @@ public:
class rust_test_suite : public rust_test {
public:
rust_crate *crate;
array_list<rust_test*> tests;
rust_test_suite();
rust_test_suite(rust_crate *crate);
virtual ~rust_test_suite();
bool run();
};

View file

@ -0,0 +1,77 @@
#include "rust_test_runtime.h"
rust_test_runtime::rust_test_runtime() {
// TODO Auto-generated constructor stub
}
rust_test_runtime::~rust_test_runtime() {
// TODO Auto-generated destructor stub
}
#define DOMAINS 32
#define TASKS 32
void
rust_domain_test::worker::run() {
rust_handle<rust_dom> *handle = kernel->create_domain(NULL, "test");
for (int i = 0; i < TASKS; i++) {
handle->referent()->create_task(NULL, "child");
}
sync::random_sleep(1000);
kernel->destroy_domain(handle->_referent);
}
bool
rust_domain_test::run() {
rust_srv srv;
rust_kernel kernel(&srv);
array_list<worker *> workers;
for (int i = 0; i < DOMAINS; i++) {
worker *worker = new rust_domain_test::worker (&kernel);
workers.append(worker);
worker->start();
}
// We don't join the worker threads here in order to simulate ad-hoc
// termination of domains. If we join_all_domains before all domains
// are actually spawned, this could crash, thus the reason for the
// sleep below.
sync::sleep(100);
kernel.join_all_domains();
return true;
}
void task_entry() {
printf("task entry\n");
}
void
rust_task_test::worker::run() {
rust_crate *crate = parent->suite->crate;
rust_handle<rust_dom> *handle =
kernel->create_domain(crate, "test");
rust_dom *domain = handle->referent();
domain->root_task->start(crate->get_exit_task_glue(),
(uintptr_t)&task_entry, NULL, 0);
domain->start_main_loop();
kernel->destroy_domain(domain);
}
bool
rust_task_test::run() {
rust_srv srv;
rust_kernel kernel(&srv);
array_list<worker *> workers;
for (int i = 0; i < DOMAINS; i++) {
worker *worker = new rust_task_test::worker (&kernel, this);
workers.append(worker);
worker->start();
}
sync::random_sleep(1000);
kernel.join_all_domains();
return true;
}

View file

@ -0,0 +1,51 @@
#include "../rust_internal.h"
#ifndef RUST_TEST_RUNTIME_H
#define RUST_TEST_RUNTIME_H
class rust_test_runtime {
public:
rust_test_runtime();
virtual ~rust_test_runtime();
};
class rust_domain_test : public rust_test {
public:
class worker : public rust_thread {
public:
rust_kernel *kernel;
worker(rust_kernel *kernel) : kernel(kernel) {
// Nop.
}
void run();
};
bool run();
const char *name() {
return "rust_domain_test";
}
};
class rust_task_test : public rust_test {
public:
rust_test_suite *suite;
rust_task_test(rust_test_suite *suite) : suite(suite) {
// Nop.
}
class worker : public rust_thread {
public:
rust_kernel *kernel;
rust_task_test *parent;
worker(rust_kernel *kernel, rust_task_test *parent) :
kernel(kernel), parent(parent) {
// Nop.
}
void run();
};
bool run();
const char *name() {
return "rust_task_test";
}
};
#endif /* RUST_TEST_RUNTIME_H */

View file

@ -1,7 +1,7 @@
#include "../rust_internal.h"
#define COUNT 1000
#define LARGE_COUNT 100000
#define LARGE_COUNT 10000
#define THREADS 10
bool

View file

@ -18,11 +18,9 @@ public:
class rust_synchronized_indexed_list_test : public rust_test {
public:
rust_srv srv;
memory_region region;
synchronized_indexed_list<indexed_list_element<int> > list;
rust_synchronized_indexed_list_test() :
region(&srv, false), list(&region) {
rust_synchronized_indexed_list_test() {
// Nop.
}

View file

@ -3,7 +3,6 @@
#include <assert.h>
#include "array_list.h"
#include "../memory_region.h"
class indexed_list_object {
public:
@ -28,12 +27,14 @@ public:
* object inserted in this list must define a "int32_t list_index" member.
*/
template<typename T> class indexed_list {
memory_region *region;
array_list<T*> list;
public:
indexed_list(memory_region *region) : region(region) {}
virtual int32_t append(T *value);
virtual bool pop(T **value);
/**
* Same as pop(), except that it returns NULL if the list is empty.
*/
virtual T* pop_value();
virtual size_t length() {
return list.size();
}
@ -76,6 +77,15 @@ indexed_list<T>::pop(T **value) {
return list.pop(value);
}
template<typename T> T*
indexed_list<T>::pop_value() {
T *value = NULL;
if (list.pop(&value)) {
return value;
}
return NULL;
}
template <typename T> T *
indexed_list<T>::operator[](int32_t index) {
T *value = list[index];

View file

@ -2,14 +2,14 @@
#define SYNCHRONIZED_INDEXED_LIST_H
#include "indexed_list.h"
#include "../sync/lock_and_signal.h"
template<typename T> class synchronized_indexed_list :
public indexed_list<T> {
lock_and_signal _lock;
public:
synchronized_indexed_list(memory_region *region) :
indexed_list<T>(region) {
synchronized_indexed_list() {
// Nop.
}