Added support for task sleeping in the scheduler.
This commit is contained in:
11
src/Makefile
11
src/Makefile
@@ -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,8 +245,9 @@ 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/spin_lock.cpp \
|
rt/sync/sync.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 \
|
||||||
rt/rust.cpp \
|
rt/rust.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
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 _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;
|
||||||
|
|||||||
@@ -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++
|
||||||
|
|||||||
@@ -330,7 +330,9 @@ 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();
|
||||||
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");
|
// 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)) {
|
||||||
|
|||||||
@@ -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__
|
||||||
|
|||||||
@@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|
||||||
|
|||||||
@@ -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
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);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user