2 #include "rust_internal.h"
9 static size_t const min_stk_bytes = 0x300;
11 // Task stack segments. Heap allocated and chained together.
14 new_stk(rust_dom *dom, size_t minsz)
16 if (minsz < min_stk_bytes)
17 minsz = min_stk_bytes;
18 size_t sz = sizeof(stk_seg) + minsz;
19 stk_seg *stk = (stk_seg *)dom->malloc(sz);
20 dom->logptr("new stk", (uintptr_t)stk);
21 memset(stk, 0, sizeof(stk_seg));
22 stk->limit = (uintptr_t) &stk->data[minsz];
23 dom->logptr("stk limit", stk->limit);
25 VALGRIND_STACK_REGISTER(&stk->data[0],
31 del_stk(rust_dom *dom, stk_seg *stk)
33 VALGRIND_STACK_DEREGISTER(stk->valgrind_id);
34 dom->logptr("freeing stk segment", (uintptr_t)stk);
40 // FIXME (issue #31): ifdef by platform. This is getting absurdly
43 size_t const n_callee_saves = 4;
44 size_t const callee_save_fp = 0;
47 align_down(uintptr_t sp)
49 // There is no platform we care about that needs more than a
51 return sp & ~(16 - 1);
55 rust_task::rust_task(rust_dom *dom, rust_task *spawner) :
62 state(&dom->running_tasks),
70 dom->logptr("new task", (uintptr_t)this);
73 rust_task::~rust_task()
75 dom->log(rust_log::MEM|rust_log::TASK,
76 "~rust_task 0x%" PRIxPTR ", refcnt=%d",
77 (uintptr_t)this, refcnt);
80 for (uintptr_t fp = get_fp(); fp; fp = get_previous_fp(fp)) {
81 frame_glue_fns *glue_fns = get_frame_glue_fns(fp);
82 dom->log(rust_log::MEM|rust_log::TASK,
83 "~rust_task, frame fp=0x%" PRIxPTR ", glue_fns=0x%" PRIxPTR,
86 dom->log(rust_log::MEM|rust_log::TASK,
87 "~rust_task, mark_glue=0x%" PRIxPTR,
89 dom->log(rust_log::MEM|rust_log::TASK,
90 "~rust_task, drop_glue=0x%" PRIxPTR,
92 dom->log(rust_log::MEM|rust_log::TASK,
93 "~rust_task, reloc_glue=0x%" PRIxPTR,
94 glue_fns->reloc_glue);
99 /* FIXME: tighten this up, there are some more
100 assertions that hold at task-lifecycle events. */
101 I(dom, refcnt == 0 ||
102 (refcnt == 1 && this == dom->root_task));
110 rust_task::start(uintptr_t exit_task_glue,
111 uintptr_t spawnee_fn,
115 dom->logptr("exit-task glue", exit_task_glue);
116 dom->logptr("from spawnee", spawnee_fn);
118 // Set sp to last uintptr_t-sized cell of segment and align down.
119 rust_sp -= sizeof(uintptr_t);
120 rust_sp = align_down(rust_sp);
122 // Begin synthesizing frames. There are two: a "fully formed"
123 // exit-task frame at the top of the stack -- that pretends to be
124 // mid-execution -- and a just-starting frame beneath it that
125 // starts executing the first instruction of the spawnee. The
126 // spawnee *thinks* it was called by the exit-task frame above
127 // it. It wasn't; we put that fake frame in place here, but the
128 // illusion is enough for the spawnee to return to the exit-task
129 // frame when it's done, and exit.
130 uintptr_t *spp = (uintptr_t *)rust_sp;
132 // The exit_task_glue frame we synthesize above the frame we activate:
133 *spp-- = (uintptr_t) this; // task
134 *spp-- = (uintptr_t) 0; // output
135 *spp-- = (uintptr_t) 0; // retpc
136 for (size_t j = 0; j < n_callee_saves; ++j) {
140 // We want 'frame_base' to point to the last callee-save in this
141 // (exit-task) frame, because we're going to inject this
142 // frame-pointer into the callee-save frame pointer value in the
143 // *next* (spawnee) frame. A cheap trick, but this means the
144 // spawnee frame will restore the proper frame pointer of the glue
145 // frame as it runs its epilogue.
146 uintptr_t frame_base = (uintptr_t) (spp+1);
148 *spp-- = (uintptr_t) dom->root_crate; // crate ptr
149 *spp-- = (uintptr_t) 0; // frame_glue_fns
151 // Copy args from spawner to spawnee.
153 uintptr_t *src = (uintptr_t *)args;
154 src += 1; // spawn-call output slot
155 src += 1; // spawn-call task slot
156 // Memcpy all but the task and output pointers
157 callsz -= (2 * sizeof(uintptr_t));
158 spp = (uintptr_t*) (((uintptr_t)spp) - callsz);
159 memcpy(spp, src, callsz);
161 // Move sp down to point to task cell.
164 // We're at root, starting up.
168 // The *implicit* incoming args to the spawnee frame we're
171 *spp-- = (uintptr_t) this; // task
172 *spp-- = (uintptr_t) 0; // output addr
173 *spp-- = (uintptr_t) exit_task_glue; // retpc
175 // The context the activate_glue needs to switch stack.
176 *spp-- = (uintptr_t) spawnee_fn; // instruction to start at
177 for (size_t j = 0; j < n_callee_saves; ++j) {
178 // callee-saves to carry in when we activate
179 if (j == callee_save_fp)
185 // Back up one, we overshot where sp should be.
186 rust_sp = (uintptr_t) (spp+1);
188 dom->add_task_to_state_vec(&dom->running_tasks, this);
192 rust_task::grow(size_t n_frame_bytes)
194 stk_seg *old_stk = this->stk;
195 uintptr_t old_top = (uintptr_t) old_stk->limit;
196 uintptr_t old_bottom = (uintptr_t) &old_stk->data[0];
197 uintptr_t rust_sp_disp = old_top - this->rust_sp;
198 size_t ssz = old_top - old_bottom;
199 dom->log(rust_log::MEM|rust_log::TASK|rust_log::UPCALL,
200 "upcall_grow_task(%" PRIdPTR
201 "), old size %" PRIdPTR
202 " bytes (old lim: 0x%" PRIxPTR ")",
203 n_frame_bytes, ssz, old_top);
205 if (ssz < n_frame_bytes)
207 ssz = next_power_of_two(ssz);
209 dom->log(rust_log::MEM|rust_log::TASK, "upcall_grow_task growing stk 0x%"
210 PRIxPTR " to %d bytes", old_stk, ssz);
212 stk_seg *nstk = new_stk(dom, ssz);
213 uintptr_t new_top = (uintptr_t) &nstk->data[ssz];
214 size_t n_copy = old_top - old_bottom;
215 dom->log(rust_log::MEM|rust_log::TASK,
216 "copying %d bytes of stack from [0x%" PRIxPTR ", 0x%" PRIxPTR "]"
217 " to [0x%" PRIxPTR ", 0x%" PRIxPTR "]",
219 old_bottom, old_bottom + n_copy,
220 new_top - n_copy, new_top);
222 VALGRIND_MAKE_MEM_DEFINED((void*)old_bottom, n_copy);
223 memcpy((void*)(new_top - n_copy), (void*)old_bottom, n_copy);
225 nstk->limit = new_top;
227 this->rust_sp = new_top - rust_sp_disp;
229 dom->log(rust_log::MEM|rust_log::TASK, "processing relocations");
231 // FIXME (issue #32): this is the most ridiculously crude
232 // relocation scheme ever. Try actually, you know, writing out
233 // reloc descriptors?
235 for (uintptr_t* p = (uintptr_t*)(new_top - n_copy);
236 p < (uintptr_t*)new_top; ++p) {
237 if (old_bottom <= *p && *p < old_top) {
238 //dom->log(rust_log::MEM, "relocating pointer 0x%" PRIxPTR
239 // " by %d bytes", *p, (new_top - old_top));
241 *p += (new_top - old_top);
244 dom->log(rust_log::MEM|rust_log::TASK,
245 "processed %d relocations", n_relocs);
246 del_stk(dom, old_stk);
247 dom->logptr("grown stk limit", new_top);
251 push_onto_thread_stack(uintptr_t &sp, uintptr_t value)
253 asm("xchgl %0, %%esp\n"
257 : "0" (sp), "r" (value)
262 rust_task::run_after_return(size_t nargs, uintptr_t glue)
264 // This is only safe to call if we're the currently-running task.
267 uintptr_t sp = runtime_sp;
269 // The compiler reserves nargs + 1 word for oldsp on the stack and
271 sp = align_down(sp - nargs * sizeof(uintptr_t));
273 uintptr_t *retpc = ((uintptr_t *) sp) - 1;
274 dom->log(rust_log::TASK|rust_log::MEM,
275 "run_after_return: overwriting retpc=0x%" PRIxPTR
276 " @ runtime_sp=0x%" PRIxPTR
277 " with glue=0x%" PRIxPTR,
280 // Move the current return address (which points into rust code)
281 // onto the rust stack and pretend we just called into the glue.
282 push_onto_thread_stack(rust_sp, *retpc);
287 rust_task::run_on_resume(uintptr_t glue)
289 // This is only safe to call if we're suspended.
292 // Inject glue as resume address in the suspended frame.
293 uintptr_t* rsp = (uintptr_t*) rust_sp;
294 rsp += n_callee_saves;
295 dom->log(rust_log::TASK|rust_log::MEM,
296 "run_on_resume: overwriting retpc=0x%" PRIxPTR
297 " @ rust_sp=0x%" PRIxPTR
298 " with glue=0x%" PRIxPTR,
304 rust_task::yield(size_t nargs)
306 dom->log(rust_log::TASK,
307 "task 0x%" PRIxPTR " yielding", this);
308 run_after_return(nargs, dom->root_crate->get_yield_glue());
311 static inline uintptr_t
312 get_callee_save_fp(uintptr_t *top_of_callee_saves)
314 return top_of_callee_saves[n_callee_saves - (callee_save_fp + 1)];
319 // Note the distinction here: kill() is when you're in an upcall
320 // from task A and want to force-fail task B, you do B->kill().
321 // If you want to fail yourself you do self->fail(upcall_nargs).
322 dom->log(rust_log::TASK, "killing task 0x%" PRIxPTR, this);
323 // Unblock the task so it can unwind.
325 if (this == dom->root_task)
327 run_on_resume(dom->root_crate->get_unwind_glue());
331 rust_task::fail(size_t nargs) {
332 // See note in ::kill() regarding who should call this.
333 dom->log(rust_log::TASK, "task 0x%" PRIxPTR " failing", this);
334 // Unblock the task so it can unwind.
336 if (this == dom->root_task)
338 run_after_return(nargs, dom->root_crate->get_unwind_glue());
340 dom->log(rust_log::TASK,
342 " propagating failure to parent 0x%" PRIxPTR,
349 rust_task::notify_waiting_tasks()
351 while (waiting_tasks.length() > 0) {
352 rust_task *t = waiting_tasks.pop()->receiver;
359 rust_task::get_fp() {
360 // sp in any suspended task points to the last callee-saved reg on
362 return get_callee_save_fp((uintptr_t*)rust_sp);
366 rust_task::get_previous_fp(uintptr_t fp) {
367 // fp happens to, coincidentally (!) also point to the last
368 // callee-save on the task stack.
369 return get_callee_save_fp((uintptr_t*)fp);
373 rust_task::get_frame_glue_fns(uintptr_t fp) {
374 fp -= sizeof(uintptr_t);
375 return *((frame_glue_fns**) fp);
381 return state == &dom->running_tasks;
387 return state == &dom->blocked_tasks;
391 rust_task::blocked_on(rust_cond *on)
393 return blocked() && cond == on;
399 return state == &dom->dead_tasks;
403 rust_task::transition(ptr_vec<rust_task> *src, ptr_vec<rust_task> *dst)
405 I(dom, state == src);
406 dom->log(rust_log::TASK,
407 "task 0x%" PRIxPTR " state change '%s' -> '%s'",
409 dom->state_vec_name(src),
410 dom->state_vec_name(dst));
411 dom->remove_task_from_state_vec(src, this);
412 dom->add_task_to_state_vec(dst, this);
417 rust_task::block(rust_cond *on)
420 transition(&dom->running_tasks, &dom->blocked_tasks);
421 dom->log(rust_log::TASK,
422 "task 0x%" PRIxPTR " blocking on 0x%" PRIxPTR,
429 rust_task::wakeup(rust_cond *from)
431 transition(&dom->blocked_tasks, &dom->running_tasks);
432 I(dom, cond == from);
438 transition(&dom->running_tasks, &dom->dead_tasks);
449 rust_task::get_crate_cache(rust_crate const *curr_crate)
451 if (cache && cache->crate != curr_crate) {
452 dom->log(rust_log::TASK, "switching task crate-cache to crate 0x%"
453 PRIxPTR, curr_crate);
459 dom->log(rust_log::TASK, "fetching cache for current crate");
460 cache = dom->get_cache(curr_crate);
469 // indent-tabs-mode: nil
471 // buffer-file-coding-system: utf-8-unix
472 // compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";