Added support for task sleeping in the scheduler.
This commit is contained in:
parent
88d9a79ac8
commit
988695a96c
12 changed files with 154 additions and 12 deletions
11
src/Makefile
11
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 \
|
||||
|
|
12
src/lib/_task.rs
Normal file
12
src/lib/_task.rs
Normal 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);
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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++
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -39,6 +39,7 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
#include "sync/sync.h"
|
||||
#include "sync/timer.h"
|
||||
#include "sync/condition_variable.h"
|
||||
|
||||
#ifndef __i386__
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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
61
src/rt/sync/timer.cpp
Normal 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
25
src/rt/sync/timer.h
Normal 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 */
|
6
src/test/run-pass/task-lib.rs
Normal file
6
src/test/run-pass/task-lib.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
use std;
|
||||
import std._task;
|
||||
|
||||
fn main() {
|
||||
_task.sleep(1000000u);
|
||||
}
|
Loading…
Reference in a new issue