Added a library version of spawn. Before long, we can remove the old version.
This commit is contained in:
@@ -1,3 +1,5 @@
|
|||||||
|
import cast = unsafe::reinterpret_cast;
|
||||||
|
|
||||||
native "rust" mod rustrt {
|
native "rust" mod rustrt {
|
||||||
fn task_sleep(time_in_us: uint);
|
fn task_sleep(time_in_us: uint);
|
||||||
fn task_yield();
|
fn task_yield();
|
||||||
@@ -9,8 +11,15 @@ native "rust" mod rustrt {
|
|||||||
fn clone_chan(c: *rust_chan) -> *rust_chan;
|
fn clone_chan(c: *rust_chan) -> *rust_chan;
|
||||||
|
|
||||||
type rust_chan;
|
type rust_chan;
|
||||||
|
type rust_task;
|
||||||
|
|
||||||
fn set_min_stack(stack_size: uint);
|
fn set_min_stack(stack_size: uint);
|
||||||
|
|
||||||
|
fn new_task() -> task_id;
|
||||||
|
fn get_task_pointer(id : task_id) -> *rust_task;
|
||||||
|
fn get_task_context(id : task_id) -> *x86_registers;
|
||||||
|
fn start_task(id : task_id);
|
||||||
|
fn get_task_trampoline() -> u32;
|
||||||
}
|
}
|
||||||
|
|
||||||
type task_id = int;
|
type task_id = int;
|
||||||
@@ -54,6 +63,76 @@ fn set_min_stack(stack_size : uint) {
|
|||||||
rustrt::set_min_stack(stack_size);
|
rustrt::set_min_stack(stack_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: make this a fn~ once those are supported.
|
||||||
|
fn _spawn(thunk : -fn() -> ()) -> task_id {
|
||||||
|
let id = rustrt::new_task();
|
||||||
|
|
||||||
|
// the order of arguments are outptr, taskptr, envptr.
|
||||||
|
|
||||||
|
// In LLVM fastcall puts the first two in ecx, edx, and the rest on the
|
||||||
|
// stack.
|
||||||
|
let regs = rustrt::get_task_context(id);
|
||||||
|
|
||||||
|
// set up the task pointer
|
||||||
|
let task_ptr : u32 = cast(rustrt::get_task_pointer(id));
|
||||||
|
(*regs).edx = task_ptr;
|
||||||
|
|
||||||
|
let raw_thunk : { code: u32, env: u32 } = cast(thunk);
|
||||||
|
(*regs).eip = raw_thunk.code;
|
||||||
|
|
||||||
|
log_err #fmt("{ %u, %u }", raw_thunk.code as uint, raw_thunk.env as uint);
|
||||||
|
|
||||||
|
// okay, now we align the stack and add the environment pointer and a fake
|
||||||
|
// return address.
|
||||||
|
|
||||||
|
// -12 for the taskm output location, the env pointer
|
||||||
|
// -4 for the return address.
|
||||||
|
(*regs).esp = align_down((*regs).esp - 12u32) - 4u32;
|
||||||
|
|
||||||
|
let ra : *mutable u32 = cast((*regs).esp);
|
||||||
|
let env : *mutable u32 = cast((*regs).esp+4u32);
|
||||||
|
let tptr : *mutable u32 = cast((*regs).esp+12u32);
|
||||||
|
|
||||||
|
// put the return pointer in ecx.
|
||||||
|
(*regs).ecx = (*regs).esp + 8u32;
|
||||||
|
|
||||||
|
*tptr = task_ptr;
|
||||||
|
*env = raw_thunk.env;
|
||||||
|
*ra = rustrt::get_task_trampoline();
|
||||||
|
|
||||||
|
rustrt::start_task(id);
|
||||||
|
|
||||||
|
ret id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Who says we can't write an operating system in Rust?
|
||||||
|
type x86_registers = {
|
||||||
|
// This needs to match the structure in context.h
|
||||||
|
mutable eax : u32,
|
||||||
|
mutable ebx : u32,
|
||||||
|
mutable ecx : u32,
|
||||||
|
mutable edx : u32,
|
||||||
|
mutable ebp : u32,
|
||||||
|
mutable esi : u32,
|
||||||
|
mutable edi : u32,
|
||||||
|
mutable esp : u32,
|
||||||
|
|
||||||
|
mutable cs : u16,
|
||||||
|
mutable ds : u16,
|
||||||
|
mutable ss : u16,
|
||||||
|
mutable es : u16,
|
||||||
|
mutable fs : u16,
|
||||||
|
mutable gs : u16,
|
||||||
|
|
||||||
|
mutable eflags : u32,
|
||||||
|
mutable eip : u32
|
||||||
|
};
|
||||||
|
|
||||||
|
fn align_down(x : u32) -> u32 {
|
||||||
|
// Aligns x down to 16 bytes
|
||||||
|
x & !(15u32)
|
||||||
|
}
|
||||||
|
|
||||||
// Local Variables:
|
// Local Variables:
|
||||||
// mode: rust;
|
// mode: rust;
|
||||||
// fill-column: 78;
|
// fill-column: 78;
|
||||||
|
|||||||
@@ -22,17 +22,17 @@ swap_registers:
|
|||||||
movl 4(%esp), %eax
|
movl 4(%esp), %eax
|
||||||
//movl %eax, 0(%eax)
|
//movl %eax, 0(%eax)
|
||||||
movl %ebx, 4(%eax)
|
movl %ebx, 4(%eax)
|
||||||
//movl %ecx, 8(%eax)
|
movl %ecx, 8(%eax)
|
||||||
//movl %edx, 12(%eax)
|
movl %edx, 12(%eax)
|
||||||
movl %ebp, 16(%eax)
|
movl %ebp, 16(%eax)
|
||||||
movl %esi, 20(%eax)
|
movl %esi, 20(%eax)
|
||||||
movl %edi, 24(%eax)
|
movl %edi, 24(%eax)
|
||||||
movw %cs, 32(%eax)
|
//movl %cs, 32(%eax)
|
||||||
movw %ds, 34(%eax)
|
//movl %ds, 34(%eax)
|
||||||
movw %ss, 36(%eax)
|
//movl %ss, 36(%eax)
|
||||||
movw %es, 38(%eax)
|
//movl %es, 38(%eax)
|
||||||
movw %fs, 40(%eax)
|
//movl %fs, 40(%eax)
|
||||||
movw %gs, 42(%eax)
|
//movl %gs, 42(%eax)
|
||||||
|
|
||||||
// save the flags
|
// save the flags
|
||||||
pushf
|
pushf
|
||||||
@@ -50,23 +50,32 @@ swap_registers:
|
|||||||
|
|
||||||
movl 4(%eax), %ebx
|
movl 4(%eax), %ebx
|
||||||
// save ecx for later...
|
// save ecx for later...
|
||||||
//movl 12(%eax), %edx
|
movl 12(%eax), %edx
|
||||||
movl 16(%eax), %ebp
|
movl 16(%eax), %ebp
|
||||||
movl 20(%eax), %esi
|
movl 20(%eax), %esi
|
||||||
movl 24(%eax), %edi
|
movl 24(%eax), %edi
|
||||||
movl 28(%eax), %esp
|
movl 28(%eax), %esp
|
||||||
// We can't actually change this...
|
// We can't actually change this...
|
||||||
//movl 32(%eax), %cs
|
//movl 32(%eax), %cs
|
||||||
movw 34(%eax), %ds
|
//movl 34(%eax), %ds
|
||||||
movw 36(%eax), %ss
|
//movl 36(%eax), %ss
|
||||||
movw 38(%eax), %es
|
//movl 38(%eax), %es
|
||||||
movw 40(%eax), %fs
|
//movl 40(%eax), %fs
|
||||||
movw 42(%eax), %gs
|
//movl 42(%eax), %gs
|
||||||
|
|
||||||
// restore the flags
|
// restore the flags
|
||||||
movl 44(%eax), %ecx
|
movl 44(%eax), %ecx
|
||||||
push %ecx
|
push %ecx
|
||||||
popf
|
popf
|
||||||
|
|
||||||
|
// ok, now we can restore ecx
|
||||||
|
movl 8(%eax), %ecx
|
||||||
|
|
||||||
// Return!
|
// Return!
|
||||||
jmp *48(%eax)
|
jmp *48(%eax)
|
||||||
|
|
||||||
|
|
||||||
|
.globl task_trampoline
|
||||||
|
task_trampoline:
|
||||||
|
// This gets set up by std::task::_spawn.
|
||||||
|
call _task_exit
|
||||||
|
|||||||
@@ -18,9 +18,9 @@ struct registers_t {
|
|||||||
};
|
};
|
||||||
|
|
||||||
class context {
|
class context {
|
||||||
|
public:
|
||||||
registers_t regs;
|
registers_t regs;
|
||||||
|
|
||||||
public:
|
|
||||||
context();
|
context();
|
||||||
|
|
||||||
context *next;
|
context *next;
|
||||||
|
|||||||
@@ -707,6 +707,37 @@ get_task_id(rust_task *task) {
|
|||||||
return task->id;
|
return task->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" CDECL rust_task_id
|
||||||
|
new_task(rust_task *task) {
|
||||||
|
return task->kernel->create_task(task, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" CDECL registers_t *
|
||||||
|
get_task_context(rust_task *task, rust_task_id id) {
|
||||||
|
registers_t *regs = &task->kernel->get_task_by_id(id)->ctx.regs;
|
||||||
|
// This next line is a little dangerous.. It means we can only safely call
|
||||||
|
// this when starting a task.
|
||||||
|
regs->esp = task->rust_sp;
|
||||||
|
return regs;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" CDECL rust_task *
|
||||||
|
get_task_pointer(rust_task *task, rust_task_id id) {
|
||||||
|
return task->kernel->get_task_by_id(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" CDECL void
|
||||||
|
start_task(rust_task *task, rust_task_id id) {
|
||||||
|
task->kernel->get_task_by_id(id)->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" void *task_trampoline asm("task_trampoline");
|
||||||
|
|
||||||
|
extern "C" CDECL void **
|
||||||
|
get_task_trampoline(rust_task *task) {
|
||||||
|
return &task_trampoline;
|
||||||
|
}
|
||||||
|
|
||||||
extern "C" CDECL rust_chan *
|
extern "C" CDECL rust_chan *
|
||||||
clone_chan(rust_task *task, rust_chan *chan) {
|
clone_chan(rust_task *task, rust_chan *chan) {
|
||||||
return chan->clone(task);
|
return chan->clone(task);
|
||||||
|
|||||||
@@ -52,13 +52,6 @@ del_stk(rust_task *task, stk_seg *stk)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Tasks
|
// Tasks
|
||||||
|
|
||||||
// FIXME (issue #31): ifdef by platform. This is getting absurdly
|
|
||||||
// x86-specific.
|
|
||||||
|
|
||||||
size_t const n_callee_saves = 4;
|
|
||||||
size_t const callee_save_fp = 0;
|
|
||||||
|
|
||||||
rust_task::rust_task(rust_scheduler *sched, rust_task_list *state,
|
rust_task::rust_task(rust_scheduler *sched, rust_task_list *state,
|
||||||
rust_task *spawner, const char *name) :
|
rust_task *spawner, const char *name) :
|
||||||
ref_count(1),
|
ref_count(1),
|
||||||
@@ -115,15 +108,8 @@ struct spawn_args {
|
|||||||
};
|
};
|
||||||
|
|
||||||
extern "C" CDECL
|
extern "C" CDECL
|
||||||
void task_start_wrapper(spawn_args *a)
|
void task_exit(void *env, int rval, rust_task *task) {
|
||||||
{
|
|
||||||
rust_task *task = a->task;
|
|
||||||
int rval = 42;
|
|
||||||
|
|
||||||
a->f(&rval, task, a->a3, a->a4);
|
|
||||||
|
|
||||||
LOG(task, task, "task exited with value %d", rval);
|
LOG(task, task, "task exited with value %d", rval);
|
||||||
|
|
||||||
task->die();
|
task->die();
|
||||||
task->lock.lock();
|
task->lock.lock();
|
||||||
task->notify_tasks_waiting_to_join();
|
task->notify_tasks_waiting_to_join();
|
||||||
@@ -132,6 +118,16 @@ void task_start_wrapper(spawn_args *a)
|
|||||||
task->yield(1);
|
task->yield(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" CDECL
|
||||||
|
void task_start_wrapper(spawn_args *a)
|
||||||
|
{
|
||||||
|
rust_task *task = a->task;
|
||||||
|
int rval = 42;
|
||||||
|
|
||||||
|
a->f(&rval, task, a->a3, a->a4);
|
||||||
|
task_exit(NULL, rval, task);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
rust_task::start(uintptr_t spawnee_fn,
|
rust_task::start(uintptr_t spawnee_fn,
|
||||||
uintptr_t args)
|
uintptr_t args)
|
||||||
@@ -154,6 +150,11 @@ rust_task::start(uintptr_t spawnee_fn,
|
|||||||
|
|
||||||
ctx.call((void *)task_start_wrapper, a, sp);
|
ctx.call((void *)task_start_wrapper, a, sp);
|
||||||
|
|
||||||
|
this->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void rust_task::start()
|
||||||
|
{
|
||||||
yield_timer.reset_us(0);
|
yield_timer.reset_us(0);
|
||||||
transition(&sched->newborn_tasks, &sched->running_tasks);
|
transition(&sched->newborn_tasks, &sched->running_tasks);
|
||||||
sched->lock.signal();
|
sched->lock.signal();
|
||||||
|
|||||||
@@ -109,6 +109,7 @@ rust_task : public kernel_owned<rust_task>, rust_cond
|
|||||||
|
|
||||||
void start(uintptr_t spawnee_fn,
|
void start(uintptr_t spawnee_fn,
|
||||||
uintptr_t args);
|
uintptr_t args);
|
||||||
|
void start();
|
||||||
void grow(size_t n_frame_bytes);
|
void grow(size_t n_frame_bytes);
|
||||||
bool running();
|
bool running();
|
||||||
bool blocked();
|
bool blocked();
|
||||||
|
|||||||
@@ -27,7 +27,10 @@ do_gc
|
|||||||
drop_chan
|
drop_chan
|
||||||
drop_port
|
drop_port
|
||||||
get_port_id
|
get_port_id
|
||||||
|
get_task_context
|
||||||
get_task_id
|
get_task_id
|
||||||
|
get_task_pointer
|
||||||
|
get_task_trampoline
|
||||||
get_time
|
get_time
|
||||||
hack_allow_leaks
|
hack_allow_leaks
|
||||||
ivec_copy_from_buf
|
ivec_copy_from_buf
|
||||||
@@ -40,6 +43,7 @@ last_os_error
|
|||||||
nano_time
|
nano_time
|
||||||
new_chan
|
new_chan
|
||||||
new_port
|
new_port
|
||||||
|
new_task
|
||||||
pin_task
|
pin_task
|
||||||
port_recv
|
port_recv
|
||||||
unpin_task
|
unpin_task
|
||||||
@@ -61,6 +65,7 @@ set_min_stack
|
|||||||
sched_threads
|
sched_threads
|
||||||
size_of
|
size_of
|
||||||
squareroot
|
squareroot
|
||||||
|
start_task
|
||||||
str_alloc
|
str_alloc
|
||||||
str_buf
|
str_buf
|
||||||
str_byte_len
|
str_byte_len
|
||||||
|
|||||||
@@ -33,3 +33,8 @@ fn test_send_recv() {
|
|||||||
assert (task::recv(p) == 10);
|
assert (task::recv(p) == 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lib_spawn() {
|
||||||
|
fn foo() { log_err "Hello, World!"; }
|
||||||
|
task::_spawn(foo);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user