Skip to content

Commit 25ae3d6

Browse files
committed
Rewrite spawn yet again
The motivation here is that the bottom of each stack needs to contain a C++ try/catch block so that we can unwind. This is already the case for main, but not spawned tasks. Issue #236
1 parent 1bd6270 commit 25ae3d6

File tree

8 files changed

+85
-92
lines changed

8 files changed

+85
-92
lines changed

src/lib/task.rs

+15-62
Original file line numberDiff line numberDiff line change
@@ -39,20 +39,17 @@ native "rust" mod rustrt {
3939
fn new_task() -> task_id;
4040
fn drop_task(task: *rust_task);
4141
fn get_task_pointer(id: task_id) -> *rust_task;
42-
fn start_task(id: task_id);
4342
fn get_task_trampoline() -> u32;
4443

4544
fn migrate_alloc(alloc: *u8, target: task_id);
45+
fn start_task(id: task_id, closure: *u8);
4646
}
4747

4848
type rust_task =
4949
{id: task,
5050
mutable notify_enabled: u32,
5151
mutable notify_chan: comm::chan<task_notification>,
52-
ctx: task_context,
53-
stack_ptr: *u8};
54-
55-
type task_context = {regs: x86_registers, next: *u8};
52+
mutable stack_ptr: *u8};
5653

5754
resource rust_task_ptr(task: *rust_task) { rustrt::drop_task(task); }
5855

@@ -115,20 +112,22 @@ fn spawn_inner(thunk: -fn(), notify: option<comm::chan<task_notification>>) ->
115112
task_id {
116113
let id = rustrt::new_task();
117114

118-
// the order of arguments are outptr, taskptr, envptr.
119-
// LLVM fastcall puts the first two in ecx, edx, and the rest on the
120-
// stack.
115+
let raw_thunk: {code: u32, env: u32} = cast(thunk);
121116

122117
// set up the task pointer
123118
let task_ptr = rust_task_ptr(rustrt::get_task_pointer(id));
124-
let regs = ptr::addr_of((**task_ptr).ctx.regs);
125-
(*regs).edx = cast(*task_ptr);;
126-
(*regs).esp = cast((**task_ptr).stack_ptr);
127119

128120
assert (ptr::null() != (**task_ptr).stack_ptr);
129121

130-
let raw_thunk: {code: u32, env: u32} = cast(thunk);
131-
(*regs).eip = raw_thunk.code;
122+
// copy the thunk from our stack to the new stack
123+
let sp: uint = cast((**task_ptr).stack_ptr);
124+
let ptrsize = sys::size_of::<*u8>();
125+
let thunkfn: *mutable uint = cast(sp - ptrsize * 2u);
126+
let thunkenv: *mutable uint = cast(sp - ptrsize);
127+
*thunkfn = cast(raw_thunk.code);
128+
*thunkenv = cast(raw_thunk.env);
129+
// align the stack to 16 bytes
130+
(**task_ptr).stack_ptr = cast(sp - ptrsize * 4u);
132131

133132
// set up notifications if they are enabled.
134133
alt notify {
@@ -139,60 +138,14 @@ fn spawn_inner(thunk: -fn(), notify: option<comm::chan<task_notification>>) ->
139138
none { }
140139
};
141140

142-
// okay, now we align the stack and add the environment pointer and a fake
143-
// return address.
144-
145-
// -12 for the taskm output location, the env pointer
146-
// -4 for the return address.
147-
(*regs).esp = align_down((*regs).esp - 12u32) - 4u32;
148-
149-
let ra: *mutable u32 = cast((*regs).esp);
150-
let env: *mutable u32 = cast((*regs).esp + 4u32);
151-
let tptr: *mutable u32 = cast((*regs).esp + 12u32);
152-
153-
// put the return pointer in ecx.
154-
(*regs).ecx = (*regs).esp + 8u32;;
155-
156-
*tptr = cast(*task_ptr);;
157-
*env = raw_thunk.env;;
158-
*ra = rustrt::get_task_trampoline();
159-
141+
// give the thunk environment's allocation to the new task
160142
rustrt::migrate_alloc(cast(raw_thunk.env), id);
161-
rustrt::start_task(id);
162-
143+
rustrt::start_task(id, cast(thunkfn));
144+
// don't cleanup the thunk in this task
163145
unsafe::leak(thunk);
164-
165146
ret id;
166147
}
167148

168-
// Who says we can't write an operating system in Rust?
169-
type x86_registers =
170-
// This needs to match the structure in context.h
171-
172-
173-
{mutable eax: u32,
174-
mutable ebx: u32,
175-
mutable ecx: u32,
176-
mutable edx: u32,
177-
mutable ebp: u32,
178-
mutable esi: u32,
179-
mutable edi: u32,
180-
mutable esp: u32,
181-
mutable cs: u16,
182-
mutable ds: u16,
183-
mutable ss: u16,
184-
mutable es: u16,
185-
mutable fs: u16,
186-
mutable gs: u16,
187-
mutable eflags: u32,
188-
mutable eip: u32};
189-
190-
fn align_down(x: u32) -> u32 {
191-
192-
// Aligns x down to 16 bytes
193-
x & !15u32
194-
}
195-
196149
// Local Variables:
197150
// mode: rust;
198151
// fill-column: 78;

src/rt/arch/i386/_context.S

-8
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,3 @@ swap_registers:
7575
jmp *48(%eax)
7676

7777

78-
.globl task_trampoline
79-
task_trampoline:
80-
// This gets set up by std::task::_spawn.
81-
#if defined(__APPLE__) || defined(__WIN32__)
82-
call _task_exit
83-
#else
84-
call task_exit
85-
#endif

src/rt/main.ll.in

+15-2
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,20 @@ define void @_rust_main_wrap(i1* nocapture, %task *, %2* nocapture, %vec *)
2424
ret void
2525
}
2626

27+
%nullary_fn = type void (i1*, %task*, %2*)
28+
29+
define void @_rust_spawn_wrap(
30+
i1* nocapture, %task*, %2* nocapture, %nullary_fn* %f)
31+
{
32+
call fastcc void %f(i1* %0, %task *%1, %2* nocapture %2)
33+
ret void
34+
}
35+
36+
declare external void @set_spawn_wrapper(void (i1*, %task*, %2*, %nullary_fn*)*);
37+
2738
define i32 @"MAIN"(i32, i32) {
28-
%3 = tail call i32 @rust_start(i32 ptrtoint (void (i1*, %task*, %2*, %vec*)* @_rust_main_wrap to i32), i32 %0, i32 %1, i32 ptrtoint (%0* @_rust_crate_map_toplevel to i32))
29-
ret i32 %3
39+
call void @set_spawn_wrapper(void (i1*, %task*, %2*, %nullary_fn*)* @_rust_spawn_wrap)
40+
41+
%result = tail call i32 @rust_start(i32 ptrtoint (void (i1*, %task*, %2*, %vec*)* @_rust_main_wrap to i32), i32 %0, i32 %1, i32 ptrtoint (%0* @_rust_crate_map_toplevel to i32))
42+
ret i32 %result
3043
}

src/rt/rust_builtin.cpp

+15-9
Original file line numberDiff line numberDiff line change
@@ -440,18 +440,24 @@ get_task_pointer(rust_task *task, rust_task_id id) {
440440
return task->kernel->get_task_by_id(id);
441441
}
442442

443-
extern "C" CDECL void
444-
start_task(rust_task *task, rust_task_id id) {
445-
rust_task * target = task->kernel->get_task_by_id(id);
446-
target->start();
447-
target->deref();
443+
// FIXME: Transitional. Remove
444+
extern "C" CDECL void **
445+
get_task_trampoline(rust_task *task) {
446+
return NULL;
448447
}
449448

450-
extern "C" void *task_trampoline asm("task_trampoline");
449+
struct fn_env_pair {
450+
intptr_t f;
451+
intptr_t env;
452+
};
453+
454+
extern "C" CDECL uintptr_t get_spawn_wrapper();
451455

452-
extern "C" CDECL void **
453-
get_task_trampoline(rust_task *task) {
454-
return &task_trampoline;
456+
extern "C" CDECL void
457+
start_task(rust_task *task, rust_task_id id, fn_env_pair *f) {
458+
rust_task *target = task->kernel->get_task_by_id(id);
459+
target->start(get_spawn_wrapper(), f->f, f->env);
460+
target->deref();
455461
}
456462

457463
extern "C" CDECL void

src/rt/rust_scheduler.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,10 @@ void
4848
rust_scheduler::activate(rust_task *task) {
4949
context ctx;
5050

51-
task->user.ctx.next = &ctx;
51+
task->ctx.next = &ctx;
5252
DLOG(this, task, "descheduling...");
5353
lock.unlock();
54-
task->user.ctx.swap(ctx);
54+
task->ctx.swap(ctx);
5555
lock.lock();
5656
DLOG(this, task, "task has returned");
5757
}

src/rt/rust_task.cpp

+33-8
Original file line numberDiff line numberDiff line change
@@ -125,13 +125,13 @@ struct spawn_args {
125125
uintptr_t, uintptr_t);
126126
};
127127

128-
struct rust_closure {
128+
struct rust_closure_env {
129129
intptr_t ref_count;
130130
type_desc *td;
131131
};
132132

133133
extern "C" CDECL
134-
void task_exit(rust_closure *env, int rval, rust_task *task) {
134+
void task_exit(rust_closure_env *env, int rval, rust_task *task) {
135135
LOG(task, task, "task exited with value %d", rval);
136136
if(env) {
137137
// free the environment.
@@ -155,14 +155,32 @@ void task_start_wrapper(spawn_args *a)
155155
int rval = 42;
156156

157157
a->f(&rval, task, a->a3, a->a4);
158-
task_exit(NULL, rval, task);
158+
task_exit((rust_closure_env*)a->a3, rval, task);
159+
}
160+
161+
/* We spawn a rust (fastcc) function through a CDECL function
162+
defined in main.ll, which is built as part of each crate. These accessors
163+
allow each rust program to install that function at startup */
164+
165+
uintptr_t spawn_wrapper;
166+
167+
extern "C" CDECL void
168+
set_spawn_wrapper(uintptr_t f) {
169+
spawn_wrapper = f;
170+
}
171+
172+
extern "C" CDECL uintptr_t
173+
get_spawn_wrapper() {
174+
return spawn_wrapper;
159175
}
160176

161177
void
162178
rust_task::start(uintptr_t spawnee_fn,
163-
uintptr_t args)
179+
uintptr_t args,
180+
uintptr_t env)
164181
{
165-
LOGPTR(sched, "from spawnee", spawnee_fn);
182+
LOG(this, task, "starting task from fn 0x%" PRIxPTR
183+
" with args 0x%" PRIxPTR, spawnee_fn, args);
166184

167185
I(sched, stk->data != NULL);
168186

@@ -173,16 +191,23 @@ rust_task::start(uintptr_t spawnee_fn,
173191
spawn_args *a = (spawn_args *)sp;
174192

175193
a->task = this;
176-
a->a3 = 0;
194+
a->a3 = env;
177195
a->a4 = args;
178196
void **f = (void **)&a->f;
179197
*f = (void *)spawnee_fn;
180198

181-
user.ctx.call((void *)task_start_wrapper, a, sp);
199+
ctx.call((void *)task_start_wrapper, a, sp);
182200

183201
this->start();
184202
}
185203

204+
void
205+
rust_task::start(uintptr_t spawnee_fn,
206+
uintptr_t args)
207+
{
208+
start(spawnee_fn, args, 0);
209+
}
210+
186211
void rust_task::start()
187212
{
188213
yield_timer.reset_us(0);
@@ -213,7 +238,7 @@ rust_task::yield(size_t time_in_us) {
213238
yield_timer.reset_us(time_in_us);
214239

215240
// Return to the scheduler.
216-
user.ctx.next->swap(user.ctx);
241+
ctx.next->swap(ctx);
217242
}
218243

219244
void

src/rt/rust_task.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ struct rust_task_user {
2929
uint32_t notify_enabled; // this is way more bits than necessary, but it
3030
// simplifies the alignment.
3131
chan_handle notify_chan;
32-
context ctx;
3332
uintptr_t rust_sp; // Saved sp when not running.
3433
};
3534

@@ -55,6 +54,7 @@ rust_task : public kernel_owned<rust_task>, rust_cond
5554
RUST_ATOMIC_REFCOUNT();
5655

5756
// Fields known to the compiler.
57+
context ctx;
5858
stk_seg *stk;
5959
uintptr_t runtime_sp; // Runtime sp while task running.
6060
void *gc_alloc_chain; // Linked list of GC allocations.
@@ -118,6 +118,9 @@ rust_task : public kernel_owned<rust_task>, rust_cond
118118

119119
~rust_task();
120120

121+
void start(uintptr_t spawnee_fn,
122+
uintptr_t args,
123+
uintptr_t env);
121124
void start(uintptr_t spawnee_fn,
122125
uintptr_t args);
123126
void start();

src/rt/rustrt.def.in

+1
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ rust_run_program
5555
rust_start
5656
rust_getcwd
5757
set_min_stack
58+
set_spawn_wrapper
5859
sched_threads
5960
size_of
6061
squareroot

0 commit comments

Comments
 (0)