1 // Copyright 2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 //! Language-level runtime services that should reasonably expected
12 //! to be available 'everywhere'. Local heaps, GC, unwinding,
13 //! local storage, and logging. Even a 'freestanding' Rust would likely want
14 //! to implement this.
16 use super::local_heap::LocalHeap;
24 use libc::{c_void, uintptr_t, c_char, size_t};
26 use option::{Option, Some, None};
27 use rt::borrowck::BorrowRecord;
29 use rt::context::Context;
35 use rt::logging::StdErrLogger;
36 use rt::sched::{Scheduler, SchedHandle};
37 use rt::stack::{StackSegment, StackPool};
38 use send_str::SendStr;
39 use unstable::finally::Finally;
41 // The Task struct represents all state associated with a rust
42 // task. There are at this point two primary "subtypes" of task,
43 // however instead of using a subtype we just have a "task_type" field
44 // in the struct. This contains a pointer to another struct that holds
45 // the type-specific state.
49 priv gc: GarbageCollector,
50 storage: LocalStorage,
51 logger: Option<StdErrLogger>,
55 name: Option<SendStr>,
56 coroutine: Option<Coroutine>,
57 sched: Option<~Scheduler>,
59 // Dynamic borrowck debugging info
60 borrow_list: Option<~[BorrowRecord]>,
61 stdout_handle: Option<~Writer>,
65 GreenTask(Option<SchedHome>),
69 /// A coroutine is nothing more than a (register context, stack) pair.
70 pub struct Coroutine {
71 /// The segment of stack on which the task is currently running or
72 /// if the task is blocked, on which the task will resume
75 /// Servo needs this to be public in order to tell SpiderMonkey
76 /// about the stack bounds.
77 current_stack_segment: StackSegment,
78 /// Always valid if the task is alive and not running.
79 saved_context: Context
82 /// Some tasks have a dedicated home scheduler that they must run on.
88 pub struct GarbageCollector;
89 pub struct LocalStorage(Option<local_data::Map>);
91 /// Represents the reason for the current unwinding process
92 pub enum UnwindResult {
93 /// The task is ending successfully
96 /// The Task is failing with reason `~Any`
101 /// Returns `true` if this `UnwindResult` is a failure
103 pub fn is_failure(&self) -> bool {
110 /// Returns `true` if this `UnwindResult` is a success
112 pub fn is_success(&self) -> bool {
120 pub struct Unwinder {
126 fn to_unwind_result(&mut self) -> UnwindResult {
128 Failure(self.cause.take().unwrap())
137 // A helper to build a new task using the dynamically found
138 // scheduler and task. Only works in GreenTask context.
139 pub fn build_homed_child(stack_size: Option<uint>,
143 let f = Cell::new(f);
144 let home = Cell::new(home);
145 Local::borrow(|running_task: &mut Task| {
146 let mut sched = running_task.sched.take_unwrap();
147 let new_task = ~running_task.new_child_homed(&mut sched.stack_pool,
151 running_task.sched = Some(sched);
156 pub fn build_child(stack_size: Option<uint>, f: proc()) -> ~Task {
157 Task::build_homed_child(stack_size, f, AnySched)
160 pub fn build_homed_root(stack_size: Option<uint>,
164 let f = Cell::new(f);
165 let home = Cell::new(home);
166 Local::borrow(|running_task: &mut Task| {
167 let mut sched = running_task.sched.take_unwrap();
168 let new_task = ~Task::new_root_homed(&mut sched.stack_pool,
172 running_task.sched = Some(sched);
177 pub fn build_root(stack_size: Option<uint>, f: proc()) -> ~Task {
178 Task::build_homed_root(stack_size, f, AnySched)
181 pub fn new_sched_task() -> Task {
183 heap: LocalHeap::new(),
184 gc: GarbageCollector,
185 storage: LocalStorage(None),
187 unwinder: Unwinder { unwinding: false, cause: None },
190 coroutine: Some(Coroutine::empty()),
193 task_type: SchedTask,
199 pub fn new_root(stack_pool: &mut StackPool,
200 stack_size: Option<uint>,
201 start: proc()) -> Task {
202 Task::new_root_homed(stack_pool, stack_size, AnySched, start)
205 pub fn new_child(&mut self,
206 stack_pool: &mut StackPool,
207 stack_size: Option<uint>,
208 start: proc()) -> Task {
209 self.new_child_homed(stack_pool, stack_size, AnySched, start)
212 pub fn new_root_homed(stack_pool: &mut StackPool,
213 stack_size: Option<uint>,
215 start: proc()) -> Task {
217 heap: LocalHeap::new(),
218 gc: GarbageCollector,
219 storage: LocalStorage(None),
221 unwinder: Unwinder { unwinding: false, cause: None },
225 coroutine: Some(Coroutine::new(stack_pool, stack_size, start)),
227 task_type: GreenTask(Some(home)),
233 pub fn new_child_homed(&mut self,
234 stack_pool: &mut StackPool,
235 stack_size: Option<uint>,
237 start: proc()) -> Task {
239 heap: LocalHeap::new(),
240 gc: GarbageCollector,
241 storage: LocalStorage(None),
243 unwinder: Unwinder { unwinding: false, cause: None },
247 coroutine: Some(Coroutine::new(stack_pool, stack_size, start)),
249 task_type: GreenTask(Some(home)),
255 pub fn give_home(&mut self, new_home: SchedHome) {
256 match self.task_type {
257 GreenTask(ref mut home) => {
258 *home = Some(new_home);
261 rtabort!("type error: used SchedTask as GreenTask");
266 pub fn take_unwrap_home(&mut self) -> SchedHome {
267 match self.task_type {
268 GreenTask(ref mut home) => {
269 let out = home.take_unwrap();
273 rtabort!("type error: used SchedTask as GreenTask");
278 pub fn run(&mut self, f: ||) {
279 rtdebug!("run called on task: {}", borrow::to_uint(self));
281 // The only try/catch block in the world. Attempt to run the task's
282 // client-specified code and catch any failures.
283 self.unwinder.try(|| {
285 // Run the task main function, then do some cleanup.
288 // First, destroy task-local storage. This may run user dtors.
290 // FIXME #8302: Dear diary. I'm so tired and confused.
291 // There's some interaction in rustc between the box
292 // annihilator and the TLS dtor by which TLS is
293 // accessed from annihilated box dtors *after* TLS is
294 // destroyed. Somehow setting TLS back to null, as the
295 // old runtime did, makes this work, but I don't currently
296 // understand how. I would expect that, if the annihilator
297 // reinvokes TLS while TLS is uninitialized, that
298 // TLS would be reinitialized but never destroyed,
299 // but somehow this works. I have no idea what's going
300 // on but this seems to make things magically work. FML.
302 // (added after initial comment) A possible interaction here is
303 // that the destructors for the objects in TLS themselves invoke
304 // TLS, or possibly some destructors for those objects being
305 // annihilated invoke TLS. Sadly these two operations seemed to
306 // be intertwined, and miraculously work for now...
309 // Destroy remaining boxes. Also may run user dtors.
310 unsafe { cleanup::annihilate(); }
312 // Finally flush and destroy any output handles which the task
313 // owns. There are no boxes here, and no user destructors should
314 // run after this any more.
315 match self.stdout_handle.take() {
317 let mut handle = handle;
326 // Cleanup the dynamic borrowck debugging info
327 borrowck::clear_task_borrow_list();
329 self.death.collect_failure(self.unwinder.to_unwind_result());
330 self.destroyed = true;
333 // New utility functions for homes.
335 pub fn is_home_no_tls(&self, sched: &~Scheduler) -> bool {
336 match self.task_type {
337 GreenTask(Some(AnySched)) => { false }
338 GreenTask(Some(Sched(SchedHandle { sched_id: ref id, _}))) => {
339 *id == sched.sched_id()
342 rtabort!("task without home");
346 rtabort!("type error: expected: GreenTask, found: SchedTask");
351 pub fn homed(&self) -> bool {
352 match self.task_type {
353 GreenTask(Some(AnySched)) => { false }
354 GreenTask(Some(Sched(SchedHandle { _ }))) => { true }
356 rtabort!("task without home");
359 rtabort!("type error: expected: GreenTask, found: SchedTask");
364 // Grab both the scheduler and the task from TLS and check if the
365 // task is executing on an appropriate scheduler.
366 pub fn on_appropriate_sched() -> bool {
367 Local::borrow(|task: &mut Task| {
368 let sched_id = task.sched.get_ref().sched_id();
369 let sched_run_anything = task.sched.get_ref().run_anything;
370 match task.task_type {
371 GreenTask(Some(AnySched)) => {
372 rtdebug!("anysched task in sched check ****");
375 GreenTask(Some(Sched(SchedHandle { sched_id: ref id, _ }))) => {
376 rtdebug!("homed task in sched check ****");
380 rtabort!("task without home");
383 rtabort!("type error: expected: GreenTask, found: SchedTask");
392 rtdebug!("called drop for a task: {}", borrow::to_uint(self));
393 rtassert!(self.destroyed);
397 // Coroutines represent nothing more than a context and a stack
402 pub fn new(stack_pool: &mut StackPool,
403 stack_size: Option<uint>,
406 let stack_size = match stack_size {
408 None => env::min_stack()
410 let start = Coroutine::build_start_wrapper(start);
411 let mut stack = stack_pool.take_segment(stack_size);
412 let initial_context = Context::new(start, &mut stack);
414 current_stack_segment: stack,
415 saved_context: initial_context
419 pub fn empty() -> Coroutine {
421 current_stack_segment: StackSegment::new(0),
422 saved_context: Context::empty()
426 fn build_start_wrapper(start: proc()) -> proc() {
427 let start_cell = Cell::new(start);
428 let wrapper: proc() = || {
429 // First code after swap to this new context. Run our
433 // Again - might work while safe, or it might not.
434 Local::borrow(|sched: &mut Scheduler| {
435 sched.run_cleanup_job();
438 // To call the run method on a task we need a direct
439 // reference to it. The task is in TLS, so we can
440 // simply unsafe_borrow it to get this reference. We
441 // need to still have the task in TLS though, so we
442 // need to unsafe_borrow.
443 let task: *mut Task = Local::unsafe_borrow();
446 // N.B. Removing `start` from the start wrapper
447 // closure by emptying a cell is critical for
448 // correctness. The ~Task pointer, and in turn the
449 // closure used to initialize the first call
450 // frame, is destroyed in the scheduler context,
451 // not task context. So any captured closures must
452 // not contain user-definable dtors that expect to
453 // be in task context. By moving `start` out of
454 // the closure, all the user code goes our of
455 // scope while the task is still running.
456 let start = start_cell.take();
461 // We remove the sched from the Task in TLS right now.
462 let sched: ~Scheduler = Local::take();
463 // ... allowing us to give it away when performing a
464 // scheduling operation.
465 sched.terminate_current_task()
470 /// Destroy coroutine and try to reuse stack segment.
471 pub fn recycle(self, stack_pool: &mut StackPool) {
473 Coroutine { current_stack_segment, _ } => {
474 stack_pool.give_segment(current_stack_segment);
482 // Just a sanity check to make sure we are catching a Rust-thrown exception
483 static UNWIND_TOKEN: uintptr_t = 839147;
486 pub fn try(&mut self, f: ||) {
487 use unstable::raw::Closure;
490 let closure: Closure = transmute(f);
491 let code = transmute(closure.code);
492 let env = transmute(closure.env);
494 let token = rust_try(try_fn, code, env);
495 assert!(token == 0 || token == UNWIND_TOKEN);
498 extern fn try_fn(code: *c_void, env: *c_void) {
500 let closure: Closure = Closure {
501 code: transmute(code),
504 let closure: || = transmute(closure);
510 fn rust_try(f: extern "C" fn(*c_void, *c_void),
512 data: *c_void) -> uintptr_t;
516 pub fn begin_unwind(&mut self, cause: ~Any) -> ! {
517 self.unwinding = true;
518 self.cause = Some(cause);
520 rust_begin_unwind(UNWIND_TOKEN);
521 return transmute(());
524 fn rust_begin_unwind(token: uintptr_t);
529 /// This function is invoked from rust's current __morestack function. Segmented
530 /// stacks are currently not enabled as segmented stacks, but rather one giant
531 /// stack segment. This means that whenever we run out of stack, we want to
532 /// truly consider it to be stack overflow rather than allocating a new stack.
533 #[no_mangle] // - this is called from C code
534 #[no_split_stack] // - it would be sad for this function to trigger __morestack
535 #[doc(hidden)] // - Function must be `pub` to get exported, but it's
536 // irrelevant for documentation purposes.
537 pub extern "C" fn rust_stack_exhausted() {
538 use rt::in_green_task_context;
540 use rt::local::Local;
541 use unstable::intrinsics;
544 // We're calling this function because the stack just ran out. We need
545 // to call some other rust functions, but if we invoke the functions
546 // right now it'll just trigger this handler being called again. In
547 // order to alleviate this, we move the stack limit to be inside of the
548 // red zone that was allocated for exactly this reason.
549 let limit = context::get_sp_limit();
550 context::record_sp_limit(limit - context::RED_ZONE / 2);
552 // This probably isn't the best course of action. Ideally one would want
553 // to unwind the stack here instead of just aborting the entire process.
554 // This is a tricky problem, however. There's a few things which need to
557 // 1. We're here because of a stack overflow, yet unwinding will run
558 // destructors and hence arbitrary code. What if that code overflows
559 // the stack? One possibility is to use the above allocation of an
560 // extra 10k to hope that we don't hit the limit, and if we do then
561 // abort the whole program. Not the best, but kind of hard to deal
562 // with unless we want to switch stacks.
564 // 2. LLVM will optimize functions based on whether they can unwind or
565 // not. It will flag functions with 'nounwind' if it believes that
566 // the function cannot trigger unwinding, but if we do unwind on
567 // stack overflow then it means that we could unwind in any function
568 // anywhere. We would have to make sure that LLVM only places the
569 // nounwind flag on functions which don't call any other functions.
571 // 3. The function that overflowed may have owned arguments. These
572 // arguments need to have their destructors run, but we haven't even
573 // begun executing the function yet, so unwinding will not run the
574 // any landing pads for these functions. If this is ignored, then
575 // the arguments will just be leaked.
577 // Exactly what to do here is a very delicate topic, and is possibly
578 // still up in the air for what exactly to do. Some relevant issues:
580 // #3555 - out-of-stack failure leaks arguments
581 // #3695 - should there be a stack limit?
582 // #9855 - possible strategies which could be taken
583 // #9854 - unwinding on windows through __morestack has never worked
584 // #2361 - possible implementation of not using landing pads
586 if in_green_task_context() {
587 Local::borrow(|task: &mut Task| {
588 let n = task.name.as_ref().map(|n| n.as_slice()).unwrap_or("<unnamed>");
590 // See the message below for why this is not emitted to the
591 // task's logger. This has the additional conundrum of the
592 // logger may not be initialized just yet, meaning that an FFI
593 // call would happen to initialized it (calling out to libuv),
594 // and the FFI call needs 2MB of stack when we just ran out.
595 rterrln!("task '{}' has overflowed its stack", n);
598 rterrln!("stack overflow in non-task context");
605 /// This is the entry point of unwinding for things like lang items and such.
606 /// The arguments are normally generated by the compiler, and need to
607 /// have static lifetimes.
608 pub fn begin_unwind_raw(msg: *c_char, file: *c_char, line: size_t) -> ! {
613 fn static_char_ptr(p: *c_char) -> &'static str {
614 let s = unsafe { CString::new(p, false) };
616 Some(s) => unsafe { transmute::<&str, &'static str>(s) },
617 None => rtabort!("message wasn't utf8?")
621 let msg = static_char_ptr(msg);
622 let file = static_char_ptr(file);
624 begin_unwind(msg, file, line as uint)
627 /// This is the entry point of unwinding for fail!() and assert!().
628 pub fn begin_unwind<M: Any + Send>(msg: M, file: &'static str, line: uint) -> ! {
630 use rt::in_green_task_context;
631 use rt::local::Local;
634 use unstable::intrinsics;
638 // Note that this should be the only allocation performed in this block.
639 // Currently this means that fail!() on OOM will invoke this code path,
640 // but then again we're not really ready for failing on OOM anyway. If
641 // we do start doing this, then we should propagate this allocation to
642 // be performed in the parent of this task instead of the task that's
644 let msg = ~msg as ~Any;
647 //let msg: &Any = msg;
648 let msg_s = match msg.as_ref::<&'static str>() {
650 None => match msg.as_ref::<~str>() {
651 Some(s) => s.as_slice(),
656 if !in_green_task_context() {
657 rterrln!("failed in non-task context at '{}', {}:{}",
662 task = Local::unsafe_borrow();
663 let n = (*task).name.as_ref().map(|n| n.as_slice()).unwrap_or("<unnamed>");
665 // XXX: this should no get forcibly printed to the console, this should
666 // either be sent to the parent task (ideally), or get printed to
667 // the task's logger. Right now the logger is actually a uvio
668 // instance, which uses unkillable blocks internally for various
669 // reasons. This will cause serious trouble if the task is failing
670 // due to mismanagment of its own kill flag, so calling our own
671 // logger in its current state is a bit of a problem.
673 rterrln!("task '{}' failed at '{}', {}:{}", n, msg_s, file, line);
675 if (*task).unwinder.unwinding {
676 rtabort!("unwinding again");
680 (*task).unwinder.begin_unwind(msg);
691 do run_in_newsched_task() {
702 do run_in_newsched_task() {
703 local_data_key!(key: @~str)
704 local_data::set(key, @~"data");
705 assert!(*local_data::get(key, |k| k.map(|k| *k)).unwrap() == ~"data");
706 local_data_key!(key2: @~str)
707 local_data::set(key2, @~"data");
708 assert!(*local_data::get(key2, |k| k.map(|k| *k)).unwrap() == ~"data");
714 do run_in_newsched_task() {
715 let result = spawntask_try(||());
716 rtdebug!("trying first assert");
717 assert!(result.is_ok());
718 let result = spawntask_try(|| fail!());
719 rtdebug!("trying second assert");
720 assert!(result.is_err());
726 do run_in_uv_task() {
727 use rand::{rng, Rng};
729 let _ = r.next_u32();
735 do run_in_uv_task() {
736 info!("here i am. logging in a newsched task");
744 do run_in_newsched_task {
745 let (port, chan) = oneshot();
747 assert!(port.recv() == 10);
755 do run_in_newsched_task() {
756 let (port, chan) = stream();
758 assert!(port.recv() == 10);
763 fn comm_shared_chan() {
766 do run_in_newsched_task() {
767 let (port, chan) = stream();
768 let chan = SharedChan::new(chan);
770 assert!(port.recv() == 10);
776 use option::{Option, Some, None};
778 do run_in_newsched_task {
780 next: Option<@mut List>,
783 let a = @mut List { next: None };
784 let b = @mut List { next: Some(a) };
792 fn test_begin_unwind() { begin_unwind("cause", file!(), line!()) }