about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorMichael Bebenita <mbebenita@mozilla.com>2010-09-10 01:21:29 -0700
committerMichael Bebenita <mbebenita@mozilla.com>2010-09-10 14:38:31 -0700
commita493350eb5ab38ba8a6563f3eb4a090d257b0d3a (patch)
treedc984eaa28a55de9f05db0b961a0e67f80ca35ef /src
parentf985fded3ede8a7677ca6c9c77817d27bc9ae492 (diff)
downloadrust-a493350eb5ab38ba8a6563f3eb4a090d257b0d3a.tar.gz
rust-a493350eb5ab38ba8a6563f3eb4a090d257b0d3a.zip
Cleanup, refactoring, and some runtime tests.
Diffstat (limited to 'src')
-rw-r--r--src/Makefile4
-rw-r--r--src/rt/rust_dom.cpp93
-rw-r--r--src/rt/rust_dom.h17
-rw-r--r--src/rt/rust_internal.h7
-rw-r--r--src/rt/rust_kernel.cpp12
-rw-r--r--src/rt/rust_task.cpp38
-rw-r--r--src/rt/rust_task.h9
-rw-r--r--src/rt/rust_task_list.cpp16
-rw-r--r--src/rt/rust_task_list.h16
-rw-r--r--src/rt/rust_upcall.cpp6
-rw-r--r--src/rt/sync/sync.cpp12
-rw-r--r--src/rt/sync/sync.h2
-rw-r--r--src/rt/sync/timer.cpp5
-rw-r--r--src/rt/sync/timer.h1
-rw-r--r--src/rt/test/rust_test_harness.cpp13
-rw-r--r--src/rt/test/rust_test_harness.h3
-rw-r--r--src/rt/test/rust_test_runtime.cpp77
-rw-r--r--src/rt/test/rust_test_runtime.h51
-rw-r--r--src/rt/test/rust_test_util.cpp2
-rw-r--r--src/rt/test/rust_test_util.h4
-rw-r--r--src/rt/util/indexed_list.h16
-rw-r--r--src/rt/util/synchronized_indexed_list.h4
22 files changed, 279 insertions, 129 deletions
diff --git a/src/Makefile b/src/Makefile
index 3fbd7799d68..a524739a4a1 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -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
diff --git a/src/rt/rust_dom.cpp b/src/rt/rust_dom.cpp
index 323e9c3981b..d3b5d2cee89 100644
--- a/src/rt/rust_dom.cpp
+++ b/src/rt/rust_dom.cpp
@@ -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:
diff --git a/src/rt/rust_dom.h b/src/rt/rust_dom.h
index 3f90bb67915..8aa24192747 100644
--- a/src/rt/rust_dom.h
+++ b/src/rt/rust_dom.h
@@ -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);
 };
 
 //
diff --git a/src/rt/rust_internal.h b/src/rt/rust_internal.h
index 0913f9673a8..d1eec48bc3b 100644
--- a/src/rt/rust_internal.h
+++ b/src/rt/rust_internal.h
@@ -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:
diff --git a/src/rt/rust_kernel.cpp b/src/rt/rust_kernel.cpp
index 549b85d348c..ef4220f9129 100644
--- a/src/rt/rust_kernel.cpp
+++ b/src/rt/rust_kernel.cpp
@@ -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;
diff --git a/src/rt/rust_task.cpp b/src/rt/rust_task.cpp
index 9750258862d..3bc41b935ca 100644
--- a/src/rt/rust_task.cpp
+++ b/src/rt/rust_task.cpp
@@ -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);
 }
diff --git a/src/rt/rust_task.h b/src/rt/rust_task.h
index 7b0cfef7b76..26ba872dbf2 100644
--- a/src/rt/rust_task.h
+++ b/src/rt/rust_task.h
@@ -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);
diff --git a/src/rt/rust_task_list.cpp b/src/rt/rust_task_list.cpp
new file mode 100644
index 00000000000..635ee13667d
--- /dev/null
+++ b/src/rt/rust_task_list.cpp
@@ -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;
+    }
+}
diff --git a/src/rt/rust_task_list.h b/src/rt/rust_task_list.h
new file mode 100644
index 00000000000..b1fba75029d
--- /dev/null
+++ b/src/rt/rust_task_list.h
@@ -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 */
diff --git a/src/rt/rust_upcall.cpp b/src/rt/rust_upcall.cpp
index 4b5fa1d631b..9a5e73f8daa 100644
--- a/src/rt/rust_upcall.cpp
+++ b/src/rt/rust_upcall.cpp
@@ -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;
 }
 
diff --git a/src/rt/sync/sync.cpp b/src/rt/sync/sync.cpp
index c754392aece..be874a115f0 100644
--- a/src/rt/sync/sync.cpp
+++ b/src/rt/sync/sync.cpp
@@ -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.
 }
diff --git a/src/rt/sync/sync.h b/src/rt/sync/sync.h
index 3829dafe650..bd755e1f9fe 100644
--- a/src/rt/sync/sync.h
+++ b/src/rt/sync/sync.h
@@ -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) {
diff --git a/src/rt/sync/timer.cpp b/src/rt/sync/timer.cpp
index 0487b39796f..e6fe3688893 100644
--- a/src/rt/sync/timer.cpp
+++ b/src/rt/sync/timer.cpp
@@ -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();
diff --git a/src/rt/sync/timer.h b/src/rt/sync/timer.h
index 509fd22cc0e..aae098a1119 100644
--- a/src/rt/sync/timer.h
+++ b/src/rt/sync/timer.h
@@ -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();
diff --git a/src/rt/test/rust_test_harness.cpp b/src/rt/test/rust_test_harness.cpp
index c33e170ebe3..41000a6dbc6 100644
--- a/src/rt/test/rust_test_harness.cpp
+++ b/src/rt/test/rust_test_harness.cpp
@@ -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;
diff --git a/src/rt/test/rust_test_harness.h b/src/rt/test/rust_test_harness.h
index 401015e4508..8dd8baaadc2 100644
--- a/src/rt/test/rust_test_harness.h
+++ b/src/rt/test/rust_test_harness.h
@@ -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();
 };
diff --git a/src/rt/test/rust_test_runtime.cpp b/src/rt/test/rust_test_runtime.cpp
new file mode 100644
index 00000000000..b10be5edf40
--- /dev/null
+++ b/src/rt/test/rust_test_runtime.cpp
@@ -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;
+}
diff --git a/src/rt/test/rust_test_runtime.h b/src/rt/test/rust_test_runtime.h
new file mode 100644
index 00000000000..8d4f38ecb79
--- /dev/null
+++ b/src/rt/test/rust_test_runtime.h
@@ -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 */
diff --git a/src/rt/test/rust_test_util.cpp b/src/rt/test/rust_test_util.cpp
index 76bd7990600..2e9d764f69b 100644
--- a/src/rt/test/rust_test_util.cpp
+++ b/src/rt/test/rust_test_util.cpp
@@ -1,7 +1,7 @@
 #include "../rust_internal.h"
 
 #define COUNT 1000
-#define LARGE_COUNT 100000
+#define LARGE_COUNT 10000
 #define THREADS 10
 
 bool
diff --git a/src/rt/test/rust_test_util.h b/src/rt/test/rust_test_util.h
index fb8490cb074..41c3579043a 100644
--- a/src/rt/test/rust_test_util.h
+++ b/src/rt/test/rust_test_util.h
@@ -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.
     }
 
diff --git a/src/rt/util/indexed_list.h b/src/rt/util/indexed_list.h
index d643a050540..df887122801 100644
--- a/src/rt/util/indexed_list.h
+++ b/src/rt/util/indexed_list.h
@@ -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];
diff --git a/src/rt/util/synchronized_indexed_list.h b/src/rt/util/synchronized_indexed_list.h
index 6b5611710d7..52fabe05960 100644
--- a/src/rt/util/synchronized_indexed_list.h
+++ b/src/rt/util/synchronized_indexed_list.h
@@ -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.
     }