Added support for task sleeping in the scheduler.

This commit is contained in:
Michael Bebenita 2010-08-11 21:23:34 -07:00
parent 88d9a79ac8
commit 988695a96c
12 changed files with 154 additions and 12 deletions

View file

@ -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 \

12
src/lib/_task.rs Normal file
View file

@ -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);
}

View file

@ -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;

View file

@ -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++

View file

@ -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)) {

View file

@ -39,6 +39,7 @@ extern "C" {
#endif
#include "sync/sync.h"
#include "sync/timer.h"
#include "sync/condition_variable.h"
#ifndef __i386__

View file

@ -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());
}

View file

@ -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();

View file

@ -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);

61
src/rt/sync/timer.cpp Normal file
View file

@ -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.
}

25
src/rt/sync/timer.h Normal file
View file

@ -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 */

View file

@ -0,0 +1,6 @@
use std;
import std._task;
fn main() {
_task.sleep(1000000u);
}