From b2dad8af31d5d77e9e7da8561d87249fff216e80 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Wed, 10 Aug 2011 18:48:57 -0700 Subject: [PATCH] Added a library version of spawn. Before long, we can remove the old version. --- src/lib/task.rs | 79 +++++++++++++++++++++++++++++++++++++ src/rt/arch/i386/_context.s | 37 ++++++++++------- src/rt/arch/i386/context.h | 2 +- src/rt/rust_builtin.cpp | 31 +++++++++++++++ src/rt/rust_task.cpp | 31 ++++++++------- src/rt/rust_task.h | 1 + src/rt/rustrt.def.in | 5 +++ src/test/stdtest/task.rs | 5 +++ 8 files changed, 161 insertions(+), 30 deletions(-) diff --git a/src/lib/task.rs b/src/lib/task.rs index a4c164658a7c..a1ede2b23af3 100644 --- a/src/lib/task.rs +++ b/src/lib/task.rs @@ -1,3 +1,5 @@ +import cast = unsafe::reinterpret_cast; + native "rust" mod rustrt { fn task_sleep(time_in_us: uint); fn task_yield(); @@ -9,8 +11,15 @@ native "rust" mod rustrt { fn clone_chan(c: *rust_chan) -> *rust_chan; type rust_chan; + type rust_task; 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; @@ -54,6 +63,76 @@ fn set_min_stack(stack_size : uint) { 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: // mode: rust; // fill-column: 78; diff --git a/src/rt/arch/i386/_context.s b/src/rt/arch/i386/_context.s index 062bc7f8dd58..d4c3fc436c66 100644 --- a/src/rt/arch/i386/_context.s +++ b/src/rt/arch/i386/_context.s @@ -22,17 +22,17 @@ swap_registers: movl 4(%esp), %eax //movl %eax, 0(%eax) movl %ebx, 4(%eax) - //movl %ecx, 8(%eax) - //movl %edx, 12(%eax) + movl %ecx, 8(%eax) + movl %edx, 12(%eax) movl %ebp, 16(%eax) movl %esi, 20(%eax) movl %edi, 24(%eax) - movw %cs, 32(%eax) - movw %ds, 34(%eax) - movw %ss, 36(%eax) - movw %es, 38(%eax) - movw %fs, 40(%eax) - movw %gs, 42(%eax) + //movl %cs, 32(%eax) + //movl %ds, 34(%eax) + //movl %ss, 36(%eax) + //movl %es, 38(%eax) + //movl %fs, 40(%eax) + //movl %gs, 42(%eax) // save the flags pushf @@ -50,23 +50,32 @@ swap_registers: movl 4(%eax), %ebx // save ecx for later... - //movl 12(%eax), %edx + movl 12(%eax), %edx movl 16(%eax), %ebp movl 20(%eax), %esi movl 24(%eax), %edi movl 28(%eax), %esp // We can't actually change this... //movl 32(%eax), %cs - movw 34(%eax), %ds - movw 36(%eax), %ss - movw 38(%eax), %es - movw 40(%eax), %fs - movw 42(%eax), %gs + //movl 34(%eax), %ds + //movl 36(%eax), %ss + //movl 38(%eax), %es + //movl 40(%eax), %fs + //movl 42(%eax), %gs // restore the flags movl 44(%eax), %ecx push %ecx popf + // ok, now we can restore ecx + movl 8(%eax), %ecx + // Return! jmp *48(%eax) + + +.globl task_trampoline +task_trampoline: + // This gets set up by std::task::_spawn. + call _task_exit diff --git a/src/rt/arch/i386/context.h b/src/rt/arch/i386/context.h index 22c331b347e9..457eaf396702 100644 --- a/src/rt/arch/i386/context.h +++ b/src/rt/arch/i386/context.h @@ -18,9 +18,9 @@ struct registers_t { }; class context { +public: registers_t regs; -public: context(); context *next; diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp index a7d6a902231c..0c957cef3a94 100644 --- a/src/rt/rust_builtin.cpp +++ b/src/rt/rust_builtin.cpp @@ -707,6 +707,37 @@ get_task_id(rust_task *task) { 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 * clone_chan(rust_task *task, rust_chan *chan) { return chan->clone(task); diff --git a/src/rt/rust_task.cpp b/src/rt/rust_task.cpp index 26ffcb307a53..a03388a2a550 100644 --- a/src/rt/rust_task.cpp +++ b/src/rt/rust_task.cpp @@ -52,13 +52,6 @@ del_stk(rust_task *task, stk_seg *stk) } // 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 *spawner, const char *name) : ref_count(1), @@ -115,15 +108,8 @@ struct spawn_args { }; 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); - +void task_exit(void *env, int rval, rust_task *task) { LOG(task, task, "task exited with value %d", rval); - task->die(); task->lock.lock(); task->notify_tasks_waiting_to_join(); @@ -132,6 +118,16 @@ void task_start_wrapper(spawn_args *a) 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 rust_task::start(uintptr_t spawnee_fn, uintptr_t args) @@ -154,6 +150,11 @@ rust_task::start(uintptr_t spawnee_fn, ctx.call((void *)task_start_wrapper, a, sp); + this->start(); +} + +void rust_task::start() +{ yield_timer.reset_us(0); transition(&sched->newborn_tasks, &sched->running_tasks); sched->lock.signal(); diff --git a/src/rt/rust_task.h b/src/rt/rust_task.h index 4286aaa8b41e..5d920c515677 100644 --- a/src/rt/rust_task.h +++ b/src/rt/rust_task.h @@ -109,6 +109,7 @@ rust_task : public kernel_owned, rust_cond void start(uintptr_t spawnee_fn, uintptr_t args); + void start(); void grow(size_t n_frame_bytes); bool running(); bool blocked(); diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in index 09f4097b9c16..da7c13c93aab 100644 --- a/src/rt/rustrt.def.in +++ b/src/rt/rustrt.def.in @@ -27,7 +27,10 @@ do_gc drop_chan drop_port get_port_id +get_task_context get_task_id +get_task_pointer +get_task_trampoline get_time hack_allow_leaks ivec_copy_from_buf @@ -40,6 +43,7 @@ last_os_error nano_time new_chan new_port +new_task pin_task port_recv unpin_task @@ -61,6 +65,7 @@ set_min_stack sched_threads size_of squareroot +start_task str_alloc str_buf str_byte_len diff --git a/src/test/stdtest/task.rs b/src/test/stdtest/task.rs index 634903a34d6c..52276cc3c60d 100644 --- a/src/test/stdtest/task.rs +++ b/src/test/stdtest/task.rs @@ -33,3 +33,8 @@ fn test_send_recv() { assert (task::recv(p) == 10); } +#[test] +fn test_lib_spawn() { + fn foo() { log_err "Hello, World!"; } + task::_spawn(foo); +}