diff options
| author | bors <bors@rust-lang.org> | 2013-10-19 09:46:18 -0700 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2013-10-19 09:46:18 -0700 |
| commit | 31a209ca4251126dc03cfbb9f4bbb54f9d296d5d (patch) | |
| tree | b31873267870fb2aaf597d1e9bfdd88448e3890a /src/libstd/rt/task.rs | |
| parent | 5751794d97941176571bdf7c16f909ac9845520c (diff) | |
| parent | 6d8330afb6c925d1092f27919f61d4ce6a3fb1d4 (diff) | |
| download | rust-31a209ca4251126dc03cfbb9f4bbb54f9d296d5d.tar.gz rust-31a209ca4251126dc03cfbb9f4bbb54f9d296d5d.zip | |
auto merge of #9834 : alexcrichton/rust/morestack, r=brson
This commit re-introduces the functionality of __morestack in a way that it was
not originally anticipated. Rust does not currently have segmented stacks,
rather just large stack segments. We do not detect when these stack segments are
overrun currently, but this commit leverages __morestack in order to check this.
This commit purges a lot of the old __morestack and stack limit C++
functionality, migrating the necessary chunks to rust. The stack limit is now
entirely maintained in rust, and the "main logic bits" of __morestack are now
also implemented in rust as well.
I put my best effort into validating that this currently builds and runs successfully on osx and linux 32/64 bit, but I was unable to get this working on windows. We never did have unwinding through __morestack frames, and although I tried poking at it for a bit, I was unable to understand why we don't get unwinding right now.
A focus of this commit is to implement as much of the logic in rust as possible. This involved some liberal usage of `no_split_stack` in various locations, along with some use of the `asm!` macro (scary). I modified a bit of C++ to stop calling `record_sp_limit` because this is no longer defined in C++, rather in rust.
Another consequence of this commit is that `thread_local_storage::{get, set}` must both be flagged with `#[rust_stack]`. I've briefly looked at the implementations on osx/linux/windows to ensure that they're pretty small stacks, and I'm pretty sure that they're definitely less than 20K stacks, so we probably don't have a lot to worry about.
Other things worthy of note:
* The default stack size is now 4MB instead of 2MB. This is so that when we request 2MB to call a C function you don't immediately overflow because you have consumed any stack at all.
* `asm!` is actually pretty cool, maybe we could actually define context switching with it?
* I wanted to add links to the internet about all this jazz of storing information in TLS, but I was only able to find a link for the windows implementation. Otherwise my suggestion is just "disassemble on that arch and see what happens"
* I put my best effort forward on arm/mips to tweak __morestack correctly, we have no ability to test this so an extra set of eyes would be useful on these spots.
* This is all really tricky stuff, so I tried to put as many comments as I thought were necessary, but if anything is still unclear (or I completely forgot to take something into account), I'm willing to write more!
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 889d9bb3156..28c38ac9b53 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(); |
