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_RUNTIME := librustrt.so
CFG_STDLIB := libstd.so CFG_STDLIB := libstd.so
CFG_GCC_CFLAGS += -fPIC 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) ifeq ($(CFG_CPUTYPE), x86_64)
CFG_GCC_CFLAGS += -m32 CFG_GCC_CFLAGS += -m32
CFG_GCC_LINK_FLAGS += -m32 CFG_GCC_LINK_FLAGS += -m32
@@ -245,7 +245,8 @@ BOOT_CMXS := $(BOOT_MLS:.ml=.cmx)
BOOT_OBJS := $(BOOT_MLS:.ml=.o) BOOT_OBJS := $(BOOT_MLS:.ml=.o)
BOOT_CMIS := $(BOOT_MLS:.ml=.cmi) BOOT_CMIS := $(BOOT_MLS:.ml=.cmi)
RUNTIME_CS := rt/sync/sync.cpp \ RUNTIME_CS := rt/sync/timer.cpp \
rt/sync/sync.cpp \
rt/sync/spin_lock.cpp \ rt/sync/spin_lock.cpp \
rt/sync/lock_free_queue.cpp \ rt/sync/lock_free_queue.cpp \
rt/sync/condition_variable.cpp \ rt/sync/condition_variable.cpp \
@@ -281,7 +282,8 @@ RUNTIME_HDR := rt/globals.h \
rt/circular_buffer.h \ rt/circular_buffer.h \
rt/util/array_list.h \ rt/util/array_list.h \
rt/util/hash_map.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_INCS := -Irt/isaac -Irt/uthash
RUNTIME_OBJS := $(RUNTIME_CS:.cpp=$(CFG_OBJ_SUFFIX)) RUNTIME_OBJS := $(RUNTIME_CS:.cpp=$(CFG_OBJ_SUFFIX))
@@ -513,6 +515,7 @@ TEST_XFAILS_LLVM := $(TASK_XFAILS) \
str-concat.rs \ str-concat.rs \
str-idx.rs \ str-idx.rs \
str-lib.rs \ str-lib.rs \
task-lib.rs \
tag.rs \ tag.rs \
tail-cps.rs \ tail-cps.rs \
tail-direct.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 _io;
mod sys; mod sys;
mod _task;
// Utility modules. // Utility modules.
@@ -25,6 +26,7 @@ mod util;
auth _io = unsafe; auth _io = unsafe;
auth _str = unsafe; auth _str = unsafe;
auth _vec = unsafe; auth _vec = unsafe;
auth _task = unsafe;
auth _int.next_power_of_two = unsafe; auth _int.next_power_of_two = unsafe;
auth map.mk_hashmap = unsafe; auth map.mk_hashmap = unsafe;

View File

@@ -191,6 +191,13 @@ rand_free(rust_task *task, randctx *rctx)
task->free(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: // Local Variables:
// mode: C++ // mode: C++

View File

@@ -330,8 +330,10 @@ rust_dom::schedule_task()
if (running_tasks.length() > 0) { if (running_tasks.length() > 0) {
size_t i = rand(&rctx); size_t i = rand(&rctx);
i %= running_tasks.length(); i %= running_tasks.length();
if (running_tasks[i]->yield_timer.has_timed_out()) {
return (rust_task *)running_tasks[i]; return (rust_task *)running_tasks[i];
} }
}
// log(rust_log::DOM|rust_log::TASK, "no schedulable tasks"); // log(rust_log::DOM|rust_log::TASK, "no schedulable tasks");
return NULL; return NULL;
} }
@@ -349,8 +351,11 @@ rust_dom::log_state() {
log(rust_log::TASK, "running tasks:"); log(rust_log::TASK, "running tasks:");
for (size_t i = 0; i < running_tasks.length(); i++) { for (size_t i = 0; i < running_tasks.length(); i++) {
log(rust_log::TASK, log(rust_log::TASK,
"\t task: %s @0x%" PRIxPTR, "\t task: %s @0x%" PRIxPTR
running_tasks[i]->name, running_tasks[i]); " 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(); rust_task *scheduled_task = schedule_task();
// If we cannot schedule a task because all other live tasks // If we cannot schedule a task because all other live tasks
// are blocked, wait on a condition variable which is signaled // are blocked, yield and hopefully some progress is made in
// if progress is made in other domains. // other domains.
if (scheduled_task == NULL) { if (scheduled_task == NULL) {
if (_log.is_tracing(rust_log::TASK)) { if (_log.is_tracing(rust_log::TASK)) {

View File

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

View File

@@ -309,10 +309,16 @@ rust_task::run_on_resume(uintptr_t glue)
} }
void 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, 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()); 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_thresh;
size_t gc_alloc_accum; 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 // 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, // 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 // 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. // Save callee-saved registers and return to the main loop.
void yield(size_t nargs); 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). // Fail this task (assuming caller-on-stack is different task).
void kill(); void kill();

View File

@@ -183,6 +183,14 @@ extern "C" CDECL void upcall_yield(rust_task *task) {
task->yield(1); 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 extern "C" CDECL void
upcall_join(rust_task *task, maybe_proxy<rust_task> *target) { upcall_join(rust_task *task, maybe_proxy<rust_task> *target) {
LOG_UPCALL_ENTRY(task); 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);
}