2013-12-15 19:17:07 -06:00
|
|
|
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
|
|
|
// Implementation of Rust stack unwinding
|
|
|
|
//
|
2013-12-18 11:57:58 -06:00
|
|
|
// For background on exception handling and stack unwinding please see
|
|
|
|
// "Exception Handling in LLVM" (llvm.org/docs/ExceptionHandling.html) and
|
|
|
|
// documents linked from it.
|
2013-12-15 19:17:07 -06:00
|
|
|
// These are also good reads:
|
|
|
|
// http://theofilos.cs.columbia.edu/blog/2013/09/22/base_abi/
|
|
|
|
// http://monoinfinito.wordpress.com/series/exception-handling-in-c/
|
|
|
|
// http://www.airs.com/blog/index.php?s=exception+frames
|
|
|
|
//
|
|
|
|
// ~~~ A brief summary ~~~
|
|
|
|
// Exception handling happens in two phases: a search phase and a cleanup phase.
|
|
|
|
//
|
2013-12-18 11:57:58 -06:00
|
|
|
// In both phases the unwinder walks stack frames from top to bottom using
|
|
|
|
// information from the stack frame unwind sections of the current process's
|
|
|
|
// modules ("module" here refers to an OS module, i.e. an executable or a
|
|
|
|
// dynamic library).
|
2013-12-15 19:17:07 -06:00
|
|
|
//
|
2013-12-18 11:57:58 -06:00
|
|
|
// For each stack frame, it invokes the associated "personality routine", whose
|
|
|
|
// address is also stored in the unwind info section.
|
2013-12-15 19:17:07 -06:00
|
|
|
//
|
2013-12-18 11:57:58 -06:00
|
|
|
// In the search phase, the job of a personality routine is to examine exception
|
|
|
|
// object being thrown, and to decide whether it should be caught at that stack
|
|
|
|
// frame. Once the handler frame has been identified, cleanup phase begins.
|
2013-12-15 19:17:07 -06:00
|
|
|
//
|
2013-12-18 11:57:58 -06:00
|
|
|
// In the cleanup phase, personality routines invoke cleanup code associated
|
|
|
|
// with their stack frames (i.e. destructors). Once stack has been unwound down
|
|
|
|
// to the handler frame level, unwinding stops and the last personality routine
|
|
|
|
// transfers control to its' catch block.
|
2013-12-15 19:17:07 -06:00
|
|
|
//
|
|
|
|
// ~~~ Frame unwind info registration ~~~
|
2013-12-18 11:57:58 -06:00
|
|
|
// Each module has its' own frame unwind info section (usually ".eh_frame"), and
|
|
|
|
// unwinder needs to know about all of them in order for unwinding to be able to
|
|
|
|
// cross module boundaries.
|
2013-12-15 19:17:07 -06:00
|
|
|
//
|
2013-12-18 11:57:58 -06:00
|
|
|
// On some platforms, like Linux, this is achieved by dynamically enumerating
|
|
|
|
// currently loaded modules via the dl_iterate_phdr() API and finding all
|
|
|
|
// .eh_frame sections.
|
2013-12-15 19:17:07 -06:00
|
|
|
//
|
2013-12-18 11:57:58 -06:00
|
|
|
// Others, like Windows, require modules to actively register their unwind info
|
|
|
|
// sections by calling __register_frame_info() API at startup. In the latter
|
|
|
|
// case it is essential that there is only one copy of the unwinder runtime in
|
|
|
|
// the process. This is usually achieved by linking to the dynamic version of
|
|
|
|
// the unwind runtime.
|
2013-12-15 19:17:07 -06:00
|
|
|
//
|
|
|
|
// Currently Rust uses unwind runtime provided by libgcc.
|
|
|
|
|
2013-12-18 11:57:58 -06:00
|
|
|
use any::{Any, AnyRefExt};
|
|
|
|
use c_str::CString;
|
|
|
|
use cast;
|
2014-01-27 05:37:55 -06:00
|
|
|
use fmt;
|
2013-12-18 11:57:58 -06:00
|
|
|
use kinds::Send;
|
2014-01-31 14:35:36 -06:00
|
|
|
use mem;
|
2013-12-18 11:57:58 -06:00
|
|
|
use option::{Some, None, Option};
|
2014-01-06 12:26:11 -06:00
|
|
|
use prelude::drop;
|
2014-01-06 18:48:51 -06:00
|
|
|
use ptr::RawPtr;
|
2013-12-18 11:57:58 -06:00
|
|
|
use result::{Err, Ok};
|
|
|
|
use rt::local::Local;
|
|
|
|
use rt::task::Task;
|
|
|
|
use str::Str;
|
|
|
|
use task::TaskResult;
|
2014-02-16 01:49:08 -06:00
|
|
|
use intrinsics;
|
2013-12-18 11:57:58 -06:00
|
|
|
|
|
|
|
use uw = self::libunwind;
|
2013-12-15 19:17:07 -06:00
|
|
|
|
Rewrite channels yet again for upgradeability
This, the Nth rewrite of channels, is not a rewrite of the core logic behind
channels, but rather their API usage. In the past, we had the distinction
between oneshot, stream, and shared channels, but the most recent rewrite
dropped oneshots in favor of streams and shared channels.
This distinction of stream vs shared has shown that it's not quite what we'd
like either, and this moves the `std::comm` module in the direction of "one
channel to rule them all". There now remains only one Chan and one Port.
This new channel is actually a hybrid oneshot/stream/shared channel under the
hood in order to optimize for the use cases in question. Additionally, this also
reduces the cognitive burden of having to choose between a Chan or a SharedChan
in an API.
My simple benchmarks show no reduction in efficiency over the existing channels
today, and a 3x improvement in the oneshot case. I sadly don't have a
pre-last-rewrite compiler to test out the old old oneshots, but I would imagine
that the performance is comparable, but slightly slower (due to atomic reference
counting).
This commit also brings the bonus bugfix to channels that the pending queue of
messages are all dropped when a Port disappears rather then when both the Port
and the Chan disappear.
2014-01-08 20:31:48 -06:00
|
|
|
#[allow(dead_code)]
|
2013-12-15 19:17:07 -06:00
|
|
|
mod libunwind {
|
|
|
|
//! Unwind library interface
|
|
|
|
|
|
|
|
#[allow(non_camel_case_types)];
|
2014-01-29 16:15:30 -06:00
|
|
|
#[allow(dead_code)]; // these are just bindings
|
2013-12-15 19:17:07 -06:00
|
|
|
|
2013-12-12 15:27:26 -06:00
|
|
|
use libc::{uintptr_t};
|
2013-12-15 19:17:07 -06:00
|
|
|
|
2014-01-04 18:50:57 -06:00
|
|
|
#[cfg(not(target_arch = "arm"))]
|
2013-12-15 19:17:07 -06:00
|
|
|
#[repr(C)]
|
|
|
|
pub enum _Unwind_Action
|
|
|
|
{
|
|
|
|
_UA_SEARCH_PHASE = 1,
|
|
|
|
_UA_CLEANUP_PHASE = 2,
|
|
|
|
_UA_HANDLER_FRAME = 4,
|
|
|
|
_UA_FORCE_UNWIND = 8,
|
|
|
|
_UA_END_OF_STACK = 16,
|
|
|
|
}
|
|
|
|
|
2014-01-04 18:50:57 -06:00
|
|
|
#[cfg(target_arch = "arm")]
|
2014-01-04 01:34:15 -06:00
|
|
|
#[repr(C)]
|
|
|
|
pub enum _Unwind_State
|
|
|
|
{
|
|
|
|
_US_VIRTUAL_UNWIND_FRAME = 0,
|
|
|
|
_US_UNWIND_FRAME_STARTING = 1,
|
|
|
|
_US_UNWIND_FRAME_RESUME = 2,
|
|
|
|
_US_ACTION_MASK = 3,
|
|
|
|
_US_FORCE_UNWIND = 8,
|
|
|
|
_US_END_OF_STACK = 16
|
|
|
|
}
|
|
|
|
|
2013-12-15 19:17:07 -06:00
|
|
|
#[repr(C)]
|
|
|
|
pub enum _Unwind_Reason_Code {
|
|
|
|
_URC_NO_REASON = 0,
|
|
|
|
_URC_FOREIGN_EXCEPTION_CAUGHT = 1,
|
|
|
|
_URC_FATAL_PHASE2_ERROR = 2,
|
|
|
|
_URC_FATAL_PHASE1_ERROR = 3,
|
|
|
|
_URC_NORMAL_STOP = 4,
|
|
|
|
_URC_END_OF_STACK = 5,
|
|
|
|
_URC_HANDLER_FOUND = 6,
|
|
|
|
_URC_INSTALL_CONTEXT = 7,
|
|
|
|
_URC_CONTINUE_UNWIND = 8,
|
2014-01-04 01:34:15 -06:00
|
|
|
_URC_FAILURE = 9, // used only by ARM EABI
|
2013-12-15 19:17:07 -06:00
|
|
|
}
|
|
|
|
|
2013-12-12 15:27:26 -06:00
|
|
|
pub type _Unwind_Exception_Class = u64;
|
2013-12-15 19:17:07 -06:00
|
|
|
|
|
|
|
pub type _Unwind_Word = uintptr_t;
|
|
|
|
|
2014-01-30 22:58:04 -06:00
|
|
|
#[cfg(target_arch = "x86")]
|
|
|
|
pub static unwinder_private_data_size: int = 5;
|
|
|
|
|
|
|
|
#[cfg(target_arch = "x86_64")]
|
2014-01-04 18:50:57 -06:00
|
|
|
pub static unwinder_private_data_size: int = 2;
|
|
|
|
|
|
|
|
#[cfg(target_arch = "arm")]
|
|
|
|
pub static unwinder_private_data_size: int = 20;
|
|
|
|
|
2013-12-15 19:17:07 -06:00
|
|
|
pub struct _Unwind_Exception {
|
|
|
|
exception_class: _Unwind_Exception_Class,
|
|
|
|
exception_cleanup: _Unwind_Exception_Cleanup_Fn,
|
2014-01-04 18:50:57 -06:00
|
|
|
private: [_Unwind_Word, ..unwinder_private_data_size],
|
2013-12-15 19:17:07 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
pub enum _Unwind_Context {}
|
|
|
|
|
|
|
|
pub type _Unwind_Exception_Cleanup_Fn = extern "C" fn(unwind_code: _Unwind_Reason_Code,
|
|
|
|
exception: *_Unwind_Exception);
|
|
|
|
|
2014-02-04 10:37:07 -06:00
|
|
|
#[cfg(target_os = "linux")]
|
|
|
|
#[cfg(target_os = "freebsd")]
|
|
|
|
#[cfg(target_os = "win32")]
|
|
|
|
#[link(name = "gcc_s")]
|
|
|
|
extern {}
|
|
|
|
|
|
|
|
#[cfg(target_os = "android")]
|
|
|
|
#[link(name = "gcc")]
|
|
|
|
extern {}
|
|
|
|
|
2013-12-15 19:17:07 -06:00
|
|
|
extern "C" {
|
|
|
|
pub fn _Unwind_RaiseException(exception: *_Unwind_Exception) -> _Unwind_Reason_Code;
|
|
|
|
pub fn _Unwind_DeleteException(exception: *_Unwind_Exception);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct Unwinder {
|
2013-12-18 11:57:58 -06:00
|
|
|
priv unwinding: bool,
|
|
|
|
priv cause: Option<~Any>
|
2013-12-15 19:17:07 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Unwinder {
|
2013-12-18 11:57:58 -06:00
|
|
|
pub fn new() -> Unwinder {
|
|
|
|
Unwinder {
|
|
|
|
unwinding: false,
|
|
|
|
cause: None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn unwinding(&self) -> bool {
|
|
|
|
self.unwinding
|
|
|
|
}
|
2013-12-15 19:17:07 -06:00
|
|
|
|
|
|
|
pub fn try(&mut self, f: ||) {
|
2014-02-16 02:04:33 -06:00
|
|
|
use raw::Closure;
|
2013-12-12 15:27:26 -06:00
|
|
|
use libc::{c_void};
|
2013-12-15 19:17:07 -06:00
|
|
|
|
|
|
|
unsafe {
|
2013-12-18 11:57:58 -06:00
|
|
|
let closure: Closure = cast::transmute(f);
|
|
|
|
let ep = rust_try(try_fn, closure.code as *c_void,
|
|
|
|
closure.env as *c_void);
|
2013-12-15 19:17:07 -06:00
|
|
|
if !ep.is_null() {
|
2014-01-04 01:34:15 -06:00
|
|
|
rtdebug!("caught {}", (*ep).exception_class);
|
2013-12-18 11:57:58 -06:00
|
|
|
uw::_Unwind_DeleteException(ep);
|
2013-12-15 19:17:07 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
extern fn try_fn(code: *c_void, env: *c_void) {
|
|
|
|
unsafe {
|
2013-12-18 11:57:58 -06:00
|
|
|
let closure: || = cast::transmute(Closure {
|
|
|
|
code: code as *(),
|
|
|
|
env: env as *(),
|
|
|
|
});
|
2013-12-15 19:17:07 -06:00
|
|
|
closure();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
extern {
|
|
|
|
// Rust's try-catch
|
|
|
|
// When f(...) returns normally, the return value is null.
|
2013-12-18 11:57:58 -06:00
|
|
|
// When f(...) throws, the return value is a pointer to the caught
|
|
|
|
// exception object.
|
2013-12-15 19:17:07 -06:00
|
|
|
fn rust_try(f: extern "C" fn(*c_void, *c_void),
|
|
|
|
code: *c_void,
|
2013-12-18 11:57:58 -06:00
|
|
|
data: *c_void) -> *uw::_Unwind_Exception;
|
2013-12-15 19:17:07 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn begin_unwind(&mut self, cause: ~Any) -> ! {
|
|
|
|
rtdebug!("begin_unwind()");
|
|
|
|
|
|
|
|
self.unwinding = true;
|
|
|
|
self.cause = Some(cause);
|
|
|
|
|
2013-12-30 20:43:03 -06:00
|
|
|
rust_fail();
|
2013-12-15 19:17:07 -06:00
|
|
|
|
2013-12-30 20:43:03 -06:00
|
|
|
// An uninlined, unmangled function upon which to slap yer breakpoints
|
|
|
|
#[inline(never)]
|
|
|
|
#[no_mangle]
|
|
|
|
fn rust_fail() -> ! {
|
2013-12-15 19:17:07 -06:00
|
|
|
unsafe {
|
2013-12-30 20:43:03 -06:00
|
|
|
let exception = ~uw::_Unwind_Exception {
|
|
|
|
exception_class: rust_exception_class(),
|
|
|
|
exception_cleanup: exception_cleanup,
|
2014-01-04 18:50:57 -06:00
|
|
|
private: [0, ..uw::unwinder_private_data_size],
|
2013-12-30 20:43:03 -06:00
|
|
|
};
|
|
|
|
let error = uw::_Unwind_RaiseException(cast::transmute(exception));
|
|
|
|
rtabort!("Could not unwind stack, error = {}", error as int)
|
|
|
|
}
|
|
|
|
|
|
|
|
extern "C" fn exception_cleanup(_unwind_code: uw::_Unwind_Reason_Code,
|
|
|
|
exception: *uw::_Unwind_Exception) {
|
|
|
|
rtdebug!("exception_cleanup()");
|
|
|
|
unsafe {
|
|
|
|
let _: ~uw::_Unwind_Exception = cast::transmute(exception);
|
|
|
|
}
|
2013-12-15 19:17:07 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn result(&mut self) -> TaskResult {
|
|
|
|
if self.unwinding {
|
|
|
|
Err(self.cause.take().unwrap())
|
|
|
|
} else {
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Rust's exception class identifier. This is used by personality routines to
|
|
|
|
// determine whether the exception was thrown by their own runtime.
|
2013-12-18 11:57:58 -06:00
|
|
|
fn rust_exception_class() -> uw::_Unwind_Exception_Class {
|
|
|
|
// M O Z \0 R U S T -- vendor, language
|
|
|
|
0x4d4f5a_00_52555354
|
2013-12-15 19:17:07 -06:00
|
|
|
}
|
|
|
|
|
2013-12-18 11:57:58 -06:00
|
|
|
// We could implement our personality routine in pure Rust, however exception
|
|
|
|
// info decoding is tedious. More importantly, personality routines have to
|
|
|
|
// handle various platform quirks, which are not fun to maintain. For this
|
|
|
|
// reason, we attempt to reuse personality routine of the C language:
|
|
|
|
// __gcc_personality_v0.
|
2013-12-15 19:17:07 -06:00
|
|
|
//
|
2013-12-18 11:57:58 -06:00
|
|
|
// Since C does not support exception catching, __gcc_personality_v0 simply
|
|
|
|
// always returns _URC_CONTINUE_UNWIND in search phase, and always returns
|
|
|
|
// _URC_INSTALL_CONTEXT (i.e. "invoke cleanup code") in cleanup phase.
|
2013-12-15 19:17:07 -06:00
|
|
|
//
|
2013-12-18 11:57:58 -06:00
|
|
|
// This is pretty close to Rust's exception handling approach, except that Rust
|
|
|
|
// does have a single "catch-all" handler at the bottom of each task's stack.
|
2013-12-15 19:17:07 -06:00
|
|
|
// So we have two versions:
|
2013-12-18 11:57:58 -06:00
|
|
|
// - rust_eh_personality, used by all cleanup landing pads, which never catches,
|
|
|
|
// so the behavior of __gcc_personality_v0 is perfectly adequate there, and
|
|
|
|
// - rust_eh_personality_catch, used only by rust_try(), which always catches.
|
|
|
|
// This is achieved by overriding the return value in search phase to always
|
|
|
|
// say "catch!".
|
2013-12-15 19:17:07 -06:00
|
|
|
|
2014-01-07 00:33:37 -06:00
|
|
|
#[cfg(not(target_arch = "arm"), not(test))]
|
|
|
|
#[doc(hidden)]
|
2014-01-04 01:34:15 -06:00
|
|
|
pub mod eabi {
|
|
|
|
use uw = super::libunwind;
|
|
|
|
use libc::c_int;
|
2013-12-15 19:17:07 -06:00
|
|
|
|
2014-01-04 01:34:15 -06:00
|
|
|
extern "C" {
|
|
|
|
fn __gcc_personality_v0(version: c_int,
|
|
|
|
actions: uw::_Unwind_Action,
|
|
|
|
exception_class: uw::_Unwind_Exception_Class,
|
|
|
|
ue_header: *uw::_Unwind_Exception,
|
|
|
|
context: *uw::_Unwind_Context)
|
|
|
|
-> uw::_Unwind_Reason_Code;
|
|
|
|
}
|
|
|
|
|
|
|
|
#[lang="eh_personality"]
|
|
|
|
#[no_mangle] // so we can reference it by name from middle/trans/base.rs
|
|
|
|
pub extern "C" fn rust_eh_personality(
|
|
|
|
version: c_int,
|
|
|
|
actions: uw::_Unwind_Action,
|
|
|
|
exception_class: uw::_Unwind_Exception_Class,
|
|
|
|
ue_header: *uw::_Unwind_Exception,
|
|
|
|
context: *uw::_Unwind_Context
|
|
|
|
) -> uw::_Unwind_Reason_Code
|
|
|
|
{
|
|
|
|
unsafe {
|
|
|
|
__gcc_personality_v0(version, actions, exception_class, ue_header,
|
|
|
|
context)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[no_mangle] // referenced from rust_try.ll
|
|
|
|
pub extern "C" fn rust_eh_personality_catch(
|
|
|
|
version: c_int,
|
|
|
|
actions: uw::_Unwind_Action,
|
|
|
|
exception_class: uw::_Unwind_Exception_Class,
|
|
|
|
ue_header: *uw::_Unwind_Exception,
|
|
|
|
context: *uw::_Unwind_Context
|
|
|
|
) -> uw::_Unwind_Reason_Code
|
|
|
|
{
|
|
|
|
if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase
|
|
|
|
uw::_URC_HANDLER_FOUND // catch!
|
|
|
|
}
|
|
|
|
else { // cleanup phase
|
|
|
|
unsafe {
|
|
|
|
__gcc_personality_v0(version, actions, exception_class, ue_header,
|
|
|
|
context)
|
|
|
|
}
|
|
|
|
}
|
2013-12-15 19:17:07 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-04 01:34:15 -06:00
|
|
|
// ARM EHABI uses a slightly different personality routine signature,
|
|
|
|
// but otherwise works the same.
|
2014-01-07 00:33:37 -06:00
|
|
|
#[cfg(target_arch = "arm", not(test))]
|
2014-01-04 01:34:15 -06:00
|
|
|
pub mod eabi {
|
|
|
|
use uw = super::libunwind;
|
|
|
|
use libc::c_int;
|
|
|
|
|
|
|
|
extern "C" {
|
|
|
|
fn __gcc_personality_v0(state: uw::_Unwind_State,
|
|
|
|
ue_header: *uw::_Unwind_Exception,
|
|
|
|
context: *uw::_Unwind_Context)
|
|
|
|
-> uw::_Unwind_Reason_Code;
|
2013-12-15 19:17:07 -06:00
|
|
|
}
|
2014-01-04 01:34:15 -06:00
|
|
|
|
|
|
|
#[lang="eh_personality"]
|
|
|
|
#[no_mangle] // so we can reference it by name from middle/trans/base.rs
|
|
|
|
pub extern "C" fn rust_eh_personality(
|
|
|
|
state: uw::_Unwind_State,
|
|
|
|
ue_header: *uw::_Unwind_Exception,
|
|
|
|
context: *uw::_Unwind_Context
|
|
|
|
) -> uw::_Unwind_Reason_Code
|
|
|
|
{
|
2013-12-15 19:17:07 -06:00
|
|
|
unsafe {
|
2014-01-04 01:34:15 -06:00
|
|
|
__gcc_personality_v0(state, ue_header, context)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[no_mangle] // referenced from rust_try.ll
|
|
|
|
pub extern "C" fn rust_eh_personality_catch(
|
|
|
|
state: uw::_Unwind_State,
|
|
|
|
ue_header: *uw::_Unwind_Exception,
|
|
|
|
context: *uw::_Unwind_Context
|
|
|
|
) -> uw::_Unwind_Reason_Code
|
|
|
|
{
|
|
|
|
if (state as c_int & uw::_US_ACTION_MASK as c_int)
|
|
|
|
== uw::_US_VIRTUAL_UNWIND_FRAME as c_int { // search phase
|
|
|
|
uw::_URC_HANDLER_FOUND // catch!
|
|
|
|
}
|
|
|
|
else { // cleanup phase
|
|
|
|
unsafe {
|
|
|
|
__gcc_personality_v0(state, ue_header, context)
|
|
|
|
}
|
2013-12-15 19:17:07 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-12-12 20:01:59 -06:00
|
|
|
|
|
|
|
/// This is the entry point of unwinding for things like lang items and such.
|
|
|
|
/// The arguments are normally generated by the compiler, and need to
|
|
|
|
/// have static lifetimes.
|
2014-01-14 20:02:19 -06:00
|
|
|
#[inline(never)] #[cold] // this is the slow path, please never inline this
|
2013-12-12 15:27:26 -06:00
|
|
|
pub fn begin_unwind_raw(msg: *u8, file: *u8, line: uint) -> ! {
|
|
|
|
use libc::c_char;
|
2013-12-12 20:01:59 -06:00
|
|
|
#[inline]
|
2013-12-12 15:27:26 -06:00
|
|
|
fn static_char_ptr(p: *u8) -> &'static str {
|
|
|
|
let s = unsafe { CString::new(p as *c_char, false) };
|
2013-12-12 20:01:59 -06:00
|
|
|
match s.as_str() {
|
|
|
|
Some(s) => unsafe { cast::transmute::<&str, &'static str>(s) },
|
|
|
|
None => rtabort!("message wasn't utf8?")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let msg = static_char_ptr(msg);
|
|
|
|
let file = static_char_ptr(file);
|
|
|
|
|
|
|
|
begin_unwind(msg, file, line as uint)
|
|
|
|
}
|
|
|
|
|
2014-01-27 05:37:55 -06:00
|
|
|
/// The entry point for unwinding with a formatted message.
|
|
|
|
///
|
|
|
|
/// This is designed to reduce the amount of code required at the call
|
2014-02-17 05:53:45 -06:00
|
|
|
/// site as much as possible (so that `fail!()` has as low an impact
|
2014-01-27 05:37:55 -06:00
|
|
|
/// on (e.g.) the inlining of other functions as possible), by moving
|
|
|
|
/// the actual formatting into this shared place.
|
|
|
|
#[inline(never)] #[cold]
|
|
|
|
pub fn begin_unwind_fmt(msg: &fmt::Arguments, file: &'static str, line: uint) -> ! {
|
2014-01-27 19:19:17 -06:00
|
|
|
// We do two allocations here, unfortunately. But (a) they're
|
|
|
|
// required with the current scheme, and (b) we don't handle
|
|
|
|
// failure + OOM properly anyway (see comment in begin_unwind
|
|
|
|
// below).
|
2014-01-27 05:37:55 -06:00
|
|
|
begin_unwind_inner(~fmt::format(msg), file, line)
|
|
|
|
}
|
|
|
|
|
2013-12-12 20:01:59 -06:00
|
|
|
/// This is the entry point of unwinding for fail!() and assert!().
|
2014-01-27 01:03:37 -06:00
|
|
|
#[inline(never)] #[cold] // avoid code bloat at the call sites as much as possible
|
2013-12-12 20:01:59 -06:00
|
|
|
pub fn begin_unwind<M: Any + Send>(msg: M, file: &'static str, line: uint) -> ! {
|
2014-01-27 01:03:37 -06:00
|
|
|
// Note that this should be the only allocation performed in this code path.
|
2014-01-06 12:26:11 -06:00
|
|
|
// Currently this means that fail!() on OOM will invoke this code path,
|
|
|
|
// but then again we're not really ready for failing on OOM anyway. If
|
|
|
|
// we do start doing this, then we should propagate this allocation to
|
|
|
|
// be performed in the parent of this task instead of the task that's
|
|
|
|
// failing.
|
|
|
|
|
2014-01-27 01:03:37 -06:00
|
|
|
// see below for why we do the `Any` coercion here.
|
|
|
|
begin_unwind_inner(~msg, file, line)
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// The core of the unwinding.
|
|
|
|
///
|
|
|
|
/// This is non-generic to avoid instantiation bloat in other crates
|
|
|
|
/// (which makes compilation of small crates noticably slower). (Note:
|
|
|
|
/// we need the `Any` object anyway, we're not just creating it to
|
|
|
|
/// avoid being generic.)
|
|
|
|
///
|
|
|
|
/// Do this split took the LLVM IR line counts of `fn main() { fail!()
|
|
|
|
/// }` from ~1900/3700 (-O/no opts) to 180/590.
|
|
|
|
#[inline(never)] #[cold] // this is the slow path, please never inline this
|
|
|
|
fn begin_unwind_inner(msg: ~Any, file: &'static str, line: uint) -> ! {
|
2014-01-06 12:26:11 -06:00
|
|
|
let mut task;
|
|
|
|
{
|
|
|
|
let msg_s = match msg.as_ref::<&'static str>() {
|
|
|
|
Some(s) => *s,
|
|
|
|
None => match msg.as_ref::<~str>() {
|
|
|
|
Some(s) => s.as_slice(),
|
|
|
|
None => "~Any",
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// It is assumed that all reasonable rust code will have a local task at
|
|
|
|
// all times. This means that this `try_take` will succeed almost all of
|
|
|
|
// the time. There are border cases, however, when the runtime has
|
|
|
|
// *almost* set up the local task, but hasn't quite gotten there yet. In
|
|
|
|
// order to get some better diagnostics, we print on failure and
|
|
|
|
// immediately abort the whole process if there is no local task
|
|
|
|
// available.
|
|
|
|
let opt_task: Option<~Task> = Local::try_take();
|
|
|
|
task = match opt_task {
|
|
|
|
Some(t) => t,
|
|
|
|
None => {
|
|
|
|
rterrln!("failed at '{}', {}:{}", msg_s, file, line);
|
|
|
|
unsafe { intrinsics::abort() }
|
|
|
|
}
|
|
|
|
};
|
2013-12-12 20:01:59 -06:00
|
|
|
|
2014-01-06 12:26:11 -06:00
|
|
|
// See comments in io::stdio::with_task_stdout as to why we have to be
|
|
|
|
// careful when using an arbitrary I/O handle from the task. We
|
|
|
|
// essentially need to dance to make sure when a task is in TLS when
|
|
|
|
// running user code.
|
|
|
|
let name = task.name.take();
|
2013-12-12 20:01:59 -06:00
|
|
|
{
|
2014-01-06 12:26:11 -06:00
|
|
|
let n = name.as_ref().map(|n| n.as_slice()).unwrap_or("<unnamed>");
|
|
|
|
|
|
|
|
match task.stderr.take() {
|
|
|
|
Some(mut stderr) => {
|
|
|
|
Local::put(task);
|
2014-01-29 18:33:57 -06:00
|
|
|
// FIXME: what to do when the task printing fails?
|
|
|
|
let _err = format_args!(|args| ::fmt::writeln(stderr, args),
|
|
|
|
"task '{}' failed at '{}', {}:{}",
|
|
|
|
n, msg_s, file, line);
|
2014-01-06 12:26:11 -06:00
|
|
|
task = Local::take();
|
|
|
|
|
2014-01-31 14:35:36 -06:00
|
|
|
match mem::replace(&mut task.stderr, Some(stderr)) {
|
2014-01-06 12:26:11 -06:00
|
|
|
Some(prev) => {
|
|
|
|
Local::put(task);
|
|
|
|
drop(prev);
|
|
|
|
task = Local::take();
|
|
|
|
}
|
|
|
|
None => {}
|
|
|
|
}
|
2013-12-12 20:01:59 -06:00
|
|
|
}
|
2014-01-06 12:26:11 -06:00
|
|
|
None => {
|
2013-12-18 11:57:58 -06:00
|
|
|
rterrln!("task '{}' failed at '{}', {}:{}", n, msg_s,
|
2013-12-12 20:01:59 -06:00
|
|
|
file, line);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-01-06 12:26:11 -06:00
|
|
|
task.name = name;
|
|
|
|
|
|
|
|
if task.unwinder.unwinding {
|
|
|
|
// If a task fails while it's already unwinding then we
|
|
|
|
// have limited options. Currently our preference is to
|
|
|
|
// just abort. In the future we may consider resuming
|
|
|
|
// unwinding or otherwise exiting the task cleanly.
|
|
|
|
rterrln!("task failed during unwinding (double-failure - total drag!)")
|
|
|
|
rterrln!("rust must abort now. so sorry.");
|
|
|
|
unsafe { intrinsics::abort() }
|
|
|
|
}
|
|
|
|
}
|
2013-12-12 20:01:59 -06:00
|
|
|
|
2014-01-06 12:26:11 -06:00
|
|
|
// The unwinder won't actually use the task at all, so we put the task back
|
|
|
|
// into TLS right before we invoke the unwinder, but this means we need an
|
|
|
|
// unsafe reference back to the unwinder once it's in TLS.
|
|
|
|
Local::put(task);
|
|
|
|
unsafe {
|
|
|
|
let task: *mut Task = Local::unsafe_borrow();
|
2013-12-12 20:01:59 -06:00
|
|
|
(*task).unwinder.begin_unwind(msg);
|
|
|
|
}
|
|
|
|
}
|