From 988695a96cee1eb825435260a1874b8daa0e590a Mon Sep 17 00:00:00 2001 From: Michael Bebenita Date: Wed, 11 Aug 2010 21:23:34 -0700 Subject: [PATCH] Added support for task sleeping in the scheduler. --- src/Makefile | 11 ++++--- src/lib/_task.rs | 12 +++++++ src/lib/std.rc | 2 ++ src/rt/rust_builtin.cpp | 7 ++++ src/rt/rust_dom.cpp | 15 ++++++--- src/rt/rust_internal.h | 1 + src/rt/rust_task.cpp | 12 +++++-- src/rt/rust_task.h | 6 ++++ src/rt/rust_upcall.cpp | 8 +++++ src/rt/sync/timer.cpp | 61 +++++++++++++++++++++++++++++++++++ src/rt/sync/timer.h | 25 ++++++++++++++ src/test/run-pass/task-lib.rs | 6 ++++ 12 files changed, 154 insertions(+), 12 deletions(-) create mode 100644 src/lib/_task.rs create mode 100644 src/rt/sync/timer.cpp create mode 100644 src/rt/sync/timer.h create mode 100644 src/test/run-pass/task-lib.rs 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, 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, // 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 *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 +#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