Added a library version of spawn. Before long, we can remove the old version.

This commit is contained in:
Eric Holk 2011-08-10 18:48:57 -07:00
parent 871d1317e5
commit b2dad8af31
8 changed files with 161 additions and 30 deletions

View file

@ -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;

View file

@ -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

View file

@ -18,9 +18,9 @@ struct registers_t {
};
class context {
public:
registers_t regs;
public:
context();
context *next;

View file

@ -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);

View file

@ -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();

View file

@ -109,6 +109,7 @@ rust_task : public kernel_owned<rust_task>, rust_cond
void start(uintptr_t spawnee_fn,
uintptr_t args);
void start();
void grow(size_t n_frame_bytes);
bool running();
bool blocked();

View file

@ -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

View file

@ -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);
}