diff options
Diffstat (limited to 'src/libstd/rt/task.rs')
| -rw-r--r-- | src/libstd/rt/task.rs | 102 |
1 files changed, 94 insertions, 8 deletions
diff --git a/src/libstd/rt/task.rs b/src/libstd/rt/task.rs index d5278975d8d..1b1e4e7d426 100644 --- a/src/libstd/rt/task.rs +++ b/src/libstd/rt/task.rs @@ -29,6 +29,7 @@ use rt::logging::StdErrLogger; use super::local_heap::LocalHeap; use rt::sched::{Scheduler, SchedHandle}; use rt::stack::{StackSegment, StackPool}; +use rt::context; use rt::context::Context; use unstable::finally::Finally; use task::spawn::Taskgroup; @@ -465,6 +466,80 @@ impl Unwinder { } } +/// This function is invoked from rust's current __morestack function. Segmented +/// stacks are currently not enabled as segmented stacks, but rather one giant +/// stack segment. This means that whenever we run out of stack, we want to +/// truly consider it to be stack overflow rather than allocating a new stack. +#[no_mangle] // - this is called from C code +#[no_split_stack] // - it would be sad for this function to trigger __morestack +#[doc(hidden)] // XXX: this function shouldn't have to be `pub` to get exported + // so it can be linked against, we should have a better way + // of specifying that. +pub extern "C" fn rust_stack_exhausted() { + use rt::in_green_task_context; + use rt::task::Task; + use rt::local::Local; + use rt::logging::Logger; + use unstable::intrinsics; + + unsafe { + // We're calling this function because the stack just ran out. We need + // to call some other rust functions, but if we invoke the functions + // right now it'll just trigger this handler being called again. In + // order to alleviate this, we move the stack limit to be inside of the + // red zone that was allocated for exactly this reason. + let limit = context::get_sp_limit(); + context::record_sp_limit(limit - context::RED_ZONE / 2); + + // This probably isn't the best course of action. Ideally one would want + // to unwind the stack here instead of just aborting the entire process. + // This is a tricky problem, however. There's a few things which need to + // be considered: + // + // 1. We're here because of a stack overflow, yet unwinding will run + // destructors and hence arbitrary code. What if that code overflows + // the stack? One possibility is to use the above allocation of an + // extra 10k to hope that we don't hit the limit, and if we do then + // abort the whole program. Not the best, but kind of hard to deal + // with unless we want to switch stacks. + // + // 2. LLVM will optimize functions based on whether they can unwind or + // not. It will flag functions with 'nounwind' if it believes that + // the function cannot trigger unwinding, but if we do unwind on + // stack overflow then it means that we could unwind in any function + // anywhere. We would have to make sure that LLVM only places the + // nounwind flag on functions which don't call any other functions. + // + // 3. The function that overflowed may have owned arguments. These + // arguments need to have their destructors run, but we haven't even + // begun executing the function yet, so unwinding will not run the + // any landing pads for these functions. If this is ignored, then + // the arguments will just be leaked. + // + // Exactly what to do here is a very delicate topic, and is possibly + // still up in the air for what exactly to do. Some relevant issues: + // + // #3555 - out-of-stack failure leaks arguments + // #3695 - should there be a stack limit? + // #9855 - possible strategies which could be taken + // #9854 - unwinding on windows through __morestack has never worked + // #2361 - possible implementation of not using landing pads + + if in_green_task_context() { + do Local::borrow |task: &mut Task| { + let n = task.name.as_ref().map(|n| n.as_slice()).unwrap_or("<unnamed>"); + + format_args!(|args| { task.logger.log(args) }, + "task '{}' has overflowed its stack", n); + } + } else { + rterrln!("stack overflow in non-task context"); + } + + intrinsics::abort(); + } +} + /// This is the entry point of unwinding for things like lang items and such. /// The arguments are normally generated by the compiler. pub fn begin_unwind(msg: *c_char, file: *c_char, line: size_t) -> ! { @@ -481,22 +556,33 @@ pub fn begin_unwind(msg: *c_char, file: *c_char, line: size_t) -> ! { let msg = match msg.as_str() { Some(s) => s, None => rtabort!("message wasn't utf8?") }; - let file = match file.as_str() { - Some(s) => s, None => rtabort!("message wasn't utf8?") - }; if in_green_task_context() { // Be careful not to allocate in this block, if we're failing we may // have been failing due to a lack of memory in the first place... do Local::borrow |task: &mut Task| { let n = task.name.as_ref().map(|n| n.as_slice()).unwrap_or("<unnamed>"); - format_args!(|args| { task.logger.log(args) }, - "task '{}' failed at '{}', {}:{}", - n, msg, file, line); + + match file.as_str() { + Some(file) => { + format_args!(|args| { task.logger.log(args) }, + "task '{}' failed at '{}', {}:{}", + n, msg, file, line); + } + None => { + format_args!(|args| { task.logger.log(args) }, + "task '{}' failed at '{}'", n, msg); + } + } } } else { - rterrln!("failed in non-task context at '{}', {}:{}", - msg, file, line as int); + match file.as_str() { + Some(file) => { + rterrln!("failed in non-task context at '{}', {}:{}", + msg, file, line as int); + } + None => rterrln!("failed in non-task context at '{}'", msg), + } } let task: *mut Task = Local::unsafe_borrow(); |
