about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Bebenita <mbebenita@mozilla.com>2010-08-11 21:23:34 -0700
committerMichael Bebenita <mbebenita@mozilla.com>2010-08-11 21:24:04 -0700
commit988695a96cee1eb825435260a1874b8daa0e590a (patch)
treeee92e117a653c8c6fad100e7416afe5468073ff3
parent88d9a79ac8b05c5631efeef6a70dec35480ecaab (diff)
downloadrust-988695a96cee1eb825435260a1874b8daa0e590a.tar.gz
rust-988695a96cee1eb825435260a1874b8daa0e590a.zip
Added support for task sleeping in the scheduler.
-rw-r--r--src/Makefile11
-rw-r--r--src/lib/_task.rs12
-rw-r--r--src/lib/std.rc2
-rw-r--r--src/rt/rust_builtin.cpp7
-rw-r--r--src/rt/rust_dom.cpp15
-rw-r--r--src/rt/rust_internal.h1
-rw-r--r--src/rt/rust_task.cpp12
-rw-r--r--src/rt/rust_task.h6
-rw-r--r--src/rt/rust_upcall.cpp8
-rw-r--r--src/rt/sync/timer.cpp61
-rw-r--r--src/rt/sync/timer.h25
-rw-r--r--src/test/run-pass/task-lib.rs6
12 files changed, 154 insertions, 12 deletions
diff --git a/src/Makefile b/src/Makefile
index 63834799bfd..c10088ea0b4 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -35,7 +35,7 @@ ifeq ($(CFG_OSTYPE), Linux)
   CFG_RUNTIME := librustrt.so
   CFG_STDLIB := libstd.so
   CFG_GCC_CFLAGS += -fPIC
-  CFG_GCC_LINK_FLAGS += -shared -fPIC -ldl -lpthread
+  CFG_GCC_LINK_FLAGS += -shared -fPIC -ldl -lpthread -lrt
   ifeq ($(CFG_CPUTYPE), x86_64)
     CFG_GCC_CFLAGS += -m32
     CFG_GCC_LINK_FLAGS += -m32
@@ -245,8 +245,9 @@ BOOT_CMXS := $(BOOT_MLS:.ml=.cmx)
 BOOT_OBJS := $(BOOT_MLS:.ml=.o)
 BOOT_CMIS := $(BOOT_MLS:.ml=.cmi)
 
-RUNTIME_CS := rt/sync/sync.cpp \
-			  rt/sync/spin_lock.cpp \
+RUNTIME_CS := rt/sync/timer.cpp \
+              rt/sync/sync.cpp \
+              rt/sync/spin_lock.cpp \
               rt/sync/lock_free_queue.cpp \
               rt/sync/condition_variable.cpp \
               rt/rust.cpp \
@@ -281,7 +282,8 @@ RUNTIME_HDR := rt/globals.h \
                rt/circular_buffer.h \
                rt/util/array_list.h \
                rt/util/hash_map.h \
-               rt/sync/sync.h
+               rt/sync/sync.h \
+               rt/sync/timer.h
 
 RUNTIME_INCS := -Irt/isaac -Irt/uthash
 RUNTIME_OBJS := $(RUNTIME_CS:.cpp=$(CFG_OBJ_SUFFIX))
@@ -513,6 +515,7 @@ TEST_XFAILS_LLVM := $(TASK_XFAILS) \
                       str-concat.rs \
                       str-idx.rs \
                       str-lib.rs \
+                      task-lib.rs \
                       tag.rs \
                       tail-cps.rs \
                       tail-direct.rs \
diff --git a/src/lib/_task.rs b/src/lib/_task.rs
new file mode 100644
index 00000000000..a12c2f278fd
--- /dev/null
+++ b/src/lib/_task.rs
@@ -0,0 +1,12 @@
+native "rust" mod rustrt {
+  fn task_sleep(uint time_in_us);
+}
+
+/**
+ * Hints the scheduler to yield this task for a specified ammount of time.
+ *
+ * arg: time_in_us maximum number of microseconds to yield control for
+ */
+fn sleep(uint time_in_us) {
+  ret rustrt.task_sleep(time_in_us);
+}
\ No newline at end of file
diff --git a/src/lib/std.rc b/src/lib/std.rc
index 8c956691f98..c2a2cf0c063 100644
--- a/src/lib/std.rc
+++ b/src/lib/std.rc
@@ -15,6 +15,7 @@ mod _str;
 
 mod _io;
 mod sys;
+mod _task;
 
 // Utility modules.
 
@@ -25,6 +26,7 @@ mod util;
 auth _io = unsafe;
 auth _str = unsafe;
 auth _vec = unsafe;
+auth _task = unsafe;
 
 auth _int.next_power_of_two = unsafe;
 auth map.mk_hashmap = unsafe;
diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp
index acbb10a4bf4..596a05cc31b 100644
--- a/src/rt/rust_builtin.cpp
+++ b/src/rt/rust_builtin.cpp
@@ -191,6 +191,13 @@ rand_free(rust_task *task, randctx *rctx)
     task->free(rctx);
 }
 
+extern "C" CDECL void upcall_sleep(rust_task *task, size_t time_in_us);
+
+extern "C" CDECL void
+task_sleep(rust_task *task, size_t time_in_us) {
+    upcall_sleep(task, time_in_us);
+}
+
 //
 // Local Variables:
 // mode: C++
diff --git a/src/rt/rust_dom.cpp b/src/rt/rust_dom.cpp
index f7b8e97bd3a..1d0675602f4 100644
--- a/src/rt/rust_dom.cpp
+++ b/src/rt/rust_dom.cpp
@@ -330,7 +330,9 @@ rust_dom::schedule_task()
     if (running_tasks.length() > 0) {
         size_t i = rand(&rctx);
         i %= running_tasks.length();
-        return (rust_task *)running_tasks[i];
+        if (running_tasks[i]->yield_timer.has_timed_out()) {
+            return (rust_task *)running_tasks[i];
+        }
     }
     // log(rust_log::DOM|rust_log::TASK, "no schedulable tasks");
     return NULL;
@@ -349,8 +351,11 @@ rust_dom::log_state() {
         log(rust_log::TASK, "running tasks:");
         for (size_t i = 0; i < running_tasks.length(); i++) {
             log(rust_log::TASK,
-                "\t task: %s @0x%" PRIxPTR,
-                running_tasks[i]->name, running_tasks[i]);
+                "\t task: %s @0x%" PRIxPTR
+                " timeout: %d",
+                running_tasks[i]->name,
+                running_tasks[i],
+                running_tasks[i]->yield_timer.get_timeout());
         }
     }
 
@@ -396,8 +401,8 @@ rust_dom::start_main_loop()
         rust_task *scheduled_task = schedule_task();
 
         // If we cannot schedule a task because all other live tasks
-        // are blocked, wait on a condition variable which is signaled
-        // if progress is made in other domains.
+        // are blocked, yield and hopefully some progress is made in
+        // other domains.
 
         if (scheduled_task == NULL) {
             if (_log.is_tracing(rust_log::TASK)) {
diff --git a/src/rt/rust_internal.h b/src/rt/rust_internal.h
index 17148399bc0..05463c536d6 100644
--- a/src/rt/rust_internal.h
+++ b/src/rt/rust_internal.h
@@ -39,6 +39,7 @@ extern "C" {
 #endif
 
 #include "sync/sync.h"
+#include "sync/timer.h"
 #include "sync/condition_variable.h"
 
 #ifndef __i386__
diff --git a/src/rt/rust_task.cpp b/src/rt/rust_task.cpp
index aca8bca75c4..cd38433ed70 100644
--- a/src/rt/rust_task.cpp
+++ b/src/rt/rust_task.cpp
@@ -309,10 +309,16 @@ rust_task::run_on_resume(uintptr_t glue)
 }
 
 void
-rust_task::yield(size_t nargs)
-{
+rust_task::yield(size_t nargs) {
+    yield(nargs, 0);
+}
+
+void
+rust_task::yield(size_t nargs, size_t time_in_us) {
     log(rust_log::TASK,
-        "task %s @0x%" PRIxPTR " yielding", name, this);
+        "task %s @0x%" PRIxPTR " yielding for %d us",
+        name, this, time_in_us);
+    yield_timer.reset(time_in_us);
     run_after_return(nargs, dom->root_crate->get_yield_glue());
 }
 
diff --git a/src/rt/rust_task.h b/src/rt/rust_task.h
index 27495e2c1ad..b66ee5a1b6f 100644
--- a/src/rt/rust_task.h
+++ b/src/rt/rust_task.h
@@ -29,6 +29,9 @@ rust_task : public maybe_proxy<rust_task>,
     size_t gc_alloc_thresh;
     size_t gc_alloc_accum;
 
+    // Keeps track of the last time this task yielded.
+    timer yield_timer;
+
     // Rendezvous pointer for receiving data when blocked on a port. If we're
     // trying to read data and no data is available on any incoming channel,
     // we block on the port, and yield control to the scheduler. Since, we
@@ -88,6 +91,9 @@ rust_task : public maybe_proxy<rust_task>,
     // Save callee-saved registers and return to the main loop.
     void yield(size_t nargs);
 
+    // Yields for a specified duration of time.
+    void yield(size_t nargs, size_t time_in_ms);
+
     // Fail this task (assuming caller-on-stack is different task).
     void kill();
 
diff --git a/src/rt/rust_upcall.cpp b/src/rt/rust_upcall.cpp
index 01eaf744843..7f9f0db1c6d 100644
--- a/src/rt/rust_upcall.cpp
+++ b/src/rt/rust_upcall.cpp
@@ -183,6 +183,14 @@ extern "C" CDECL void upcall_yield(rust_task *task) {
     task->yield(1);
 }
 
+extern "C" CDECL void upcall_sleep(rust_task *task, size_t time_in_us) {
+    LOG_UPCALL_ENTRY(task);
+    task->log(rust_log::UPCALL | rust_log::TASK, "elapsed %d",
+              task->yield_timer.get_elapsed_time());
+    task->log(rust_log::UPCALL | rust_log::TASK, "sleep %d us", time_in_us);
+    task->yield(2, time_in_us);
+}
+
 extern "C" CDECL void
 upcall_join(rust_task *task, maybe_proxy<rust_task> *target) {
     LOG_UPCALL_ENTRY(task);
diff --git a/src/rt/sync/timer.cpp b/src/rt/sync/timer.cpp
new file mode 100644
index 00000000000..e45dd633314
--- /dev/null
+++ b/src/rt/sync/timer.cpp
@@ -0,0 +1,61 @@
+#include "../globals.h"
+#include "timer.h"
+
+#if defined(__APPLE__)
+#include <mach/mach_time.h>
+#endif
+
+timer::timer() {
+    reset(0);
+#if __WIN32__
+    uint64_t ticks_per_second;
+    QueryPerformanceFrequency((LARGE_INTEGER *)&ticks_per_second);
+    _ticks_per_us = ticks_per_second / 1000000;
+#endif
+}
+
+void
+timer::reset(uint64_t timeout) {
+    _start = get_time();
+    _timeout = timeout;
+}
+
+uint64_t
+timer::get_elapsed_time() {
+    return get_time() - _start;
+}
+
+int64_t
+timer::get_timeout() {
+    return _timeout - get_elapsed_time();
+}
+
+bool
+timer::has_timed_out() {
+    return get_timeout() <= 0;
+}
+
+uint64_t
+timer::get_time() {
+#ifdef __APPLE__
+    uint64_t time = mach_absolute_time();
+    mach_timebase_info_data_t info = {0, 0};
+    if (info.denom == 0) {
+        mach_timebase_info(&info);
+    }
+    uint64_t time_nano = time * (info.numer / info.denom);
+    return time_nano / 1000;
+#elif __WIN32__
+    uint64_t ticks;
+    QueryPerformanceCounter((LARGE_INTEGER *)&ticks);
+    return ticks / _ticks_per_us;
+#else
+    timespec ts;
+    clock_gettime(CLOCK_MONOTONIC, &ts);
+    return (ts.tv_sec * 1000000000LL + ts.tv_nsec) / 1000;
+#endif
+}
+
+timer::~timer() {
+    // Nop.
+}
diff --git a/src/rt/sync/timer.h b/src/rt/sync/timer.h
new file mode 100644
index 00000000000..509fd22cc0e
--- /dev/null
+++ b/src/rt/sync/timer.h
@@ -0,0 +1,25 @@
+/*
+ *  Utility class to measure time in a platform independent way.
+ */
+
+#ifndef TIMER_H
+#define TIMER_H
+
+class timer {
+private:
+    uint64_t _start;
+    uint64_t _timeout;
+    uint64_t get_time();
+#if __WIN32__
+    uint64_t _ticks_per_us;
+#endif
+public:
+    timer();
+    void reset(uint64_t timeout);
+    uint64_t get_elapsed_time();
+    int64_t get_timeout();
+    bool has_timed_out();
+    virtual ~timer();
+};
+
+#endif /* TIMER_H */
diff --git a/src/test/run-pass/task-lib.rs b/src/test/run-pass/task-lib.rs
new file mode 100644
index 00000000000..18499f4e5d0
--- /dev/null
+++ b/src/test/run-pass/task-lib.rs
@@ -0,0 +1,6 @@
+use std;
+import std._task;
+
+fn main() {
+  _task.sleep(1000000u);
+}
\ No newline at end of file