diff options
| author | bors <bors@rust-lang.org> | 2013-10-28 01:26:16 -0700 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2013-10-28 01:26:16 -0700 |
| commit | 9ef23e9060d60189894f6d725e6c1c73b887c802 (patch) | |
| tree | 0b8051814dd8a5ef08e663c172e2b456065d625d /src/libstd/rt/task.rs | |
| parent | cb5b21eba713ff3888b2741db4c9e7d841cfde02 (diff) | |
| parent | fa8e71a8257f4226ab532d4bf268d3ecbfa98eb4 (diff) | |
| download | rust-9ef23e9060d60189894f6d725e6c1c73b887c802.tar.gz rust-9ef23e9060d60189894f6d725e6c1c73b887c802.zip | |
auto merge of #9967 : Kimundi/rust/fail_cause_two, r=huonw
# Summary
This PR allows the cause of a failure to be received in a task's future result and as the `Err` case of `task::try`, and also implements dynamic typing in form of an `Any` trait.
# Task failure
- `fail!` and related macros now accept 5 kinds of types: `~str`, `&'static str`, `std::send_str::SendStr`, `~std::any::Any` and `~T` where `T: Any + Send + 'static`
- `std::task::TaskResult` got transformed into an internal enum `std::rt::task::UnwindResult`, and it's `Failure` variant now contains a value of enum type `UnwindReason`:
- `UnwindReasonStr(SendStr)` maps to failing with a value of type `~str`, `&'static str` or `SendStr`.
- `UnwindReasonAny(~Any)` maps to failing with an `~Any` or `~T` with `T: Send + 'static`.
- `UnwindReasonLinked` maps to failing because the task got killed by linked failure.
- Instead, `std::task::TaskResult` is now a typedef for `Result<(), ~Any>`, and both `TaskBuilder`'s `future_result()` and `task::try` now work with a value of this type.
- `future_result()` no longer returns a `Port<TaskResult>`, instead it returns a wrapper `TaskResultPort` that implements `GenericPort` and `Peekable`, and which lazily allocates a `~` box and casts to `~Any` in case of failure with a `SendStr` or linked failure (for the latter case a unit struct `LinkedFailure` got added.)
- Because `fail!` collapses both `~str` and `&'static str` into a `SendStr`, checking if a task error value is a string will just require a `.is::<SendStr>()` check, with `~str` and `&'static str` only being possible in case of an explicit `fail!(~~"...")` or `fail!(~ (&"...") as ~Any)`:
# Any
- In order to allow failing with arbitrary data, the `Any` trait got implemented.
- It is being used in form of a trait object, usually `~Any` or `&Any`.
- `&Any`, `~Any` and `&mut Any` have a few extension methods implemented on them:
- `is<T>(self) -> bool` returns true if the `Any` object contains type `T`
- `as_ref<T>(self) -> Option<&T>` returns a reference to the contained type, if it is `T`
- `as_mut<T>(self) -> Option<&mut T>` returns a mutable reference to the contained type, if it is `T`
- `move<T>(self) -> Option<~T>` allows to get the `~T` out of an `~Any` again.
- `Any` currently uses type descriptors as a type id for comparisons, which is
- not reliable, as it is not guaranteed that every type has only one type descriptor.
- But safe, as no two types share the same type descriptor.
- The implementation also a few `transmute`s, mostly to cast a `*Void` of the wrapped type into it's actual type `&T`, `&mut T` or `~T`.
# Other changes
- `std::unstable::UnsafeArc::try_unwrap` no longer uses `Either`, bringing us one step closer to removing that type.
- A few of the touched modules got their import lines sorted.
- A few stylistic cleanups here and there.
Diffstat (limited to 'src/libstd/rt/task.rs')
| -rw-r--r-- | src/libstd/rt/task.rs | 174 |
1 files changed, 124 insertions, 50 deletions
diff --git a/src/libstd/rt/task.rs b/src/libstd/rt/task.rs index f82eb929a39..8f695763a25 100644 --- a/src/libstd/rt/task.rs +++ b/src/libstd/rt/task.rs @@ -13,29 +13,31 @@ //! local storage, and logging. Even a 'freestanding' Rust would likely want //! to implement this. +use super::local_heap::LocalHeap; + +use prelude::*; + use borrow; use cast::transmute; +use cell::Cell; use cleanup; -use local_data; use libc::{c_void, uintptr_t, c_char, size_t}; -use prelude::*; +use local_data; use option::{Option, Some, None}; -use rt::borrowck; use rt::borrowck::BorrowRecord; +use rt::borrowck; +use rt::context::Context; +use rt::context; use rt::env; use rt::io::Writer; use rt::kill::Death; use rt::local::Local; 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; -use cell::Cell; use send_str::SendStr; +use task::spawn::Taskgroup; +use unstable::finally::Finally; // The Task struct represents all state associated with a rust // task. There are at this point two primary "subtypes" of task, @@ -85,8 +87,61 @@ pub enum SchedHome { pub struct GarbageCollector; pub struct LocalStorage(Option<local_data::Map>); +/// Represents the reason for the current unwinding process +pub enum UnwindResult { + /// The task is ending successfully + Success, + + /// The Task is failing with reason `UnwindReason` + Failure(UnwindReason), +} + +impl UnwindResult { + /// Returns `true` if this `UnwindResult` is a failure + #[inline] + pub fn is_failure(&self) -> bool { + match *self { + Success => false, + Failure(_) => true + } + } + + /// Returns `true` if this `UnwindResult` is a success + #[inline] + pub fn is_success(&self) -> bool { + match *self { + Success => true, + Failure(_) => false + } + } +} + +/// Represents the cause of a task failure +#[deriving(ToStr)] +pub enum UnwindReason { + /// Failed with a string message + UnwindReasonStr(SendStr), + + /// Failed with an `~Any` + UnwindReasonAny(~Any), + + /// Failed because of linked failure + UnwindReasonLinked +} + pub struct Unwinder { unwinding: bool, + cause: Option<UnwindReason> +} + +impl Unwinder { + fn to_unwind_result(&mut self) -> UnwindResult { + if self.unwinding { + Failure(self.cause.take().unwrap()) + } else { + Success + } + } } impl Task { @@ -135,7 +190,7 @@ impl Task { gc: GarbageCollector, storage: LocalStorage(None), logger: StdErrLogger::new(), - unwinder: Unwinder { unwinding: false }, + unwinder: Unwinder { unwinding: false, cause: None }, taskgroup: None, death: Death::new(), destroyed: false, @@ -170,7 +225,7 @@ impl Task { gc: GarbageCollector, storage: LocalStorage(None), logger: StdErrLogger::new(), - unwinder: Unwinder { unwinding: false }, + unwinder: Unwinder { unwinding: false, cause: None }, taskgroup: None, death: Death::new(), destroyed: false, @@ -193,7 +248,7 @@ impl Task { gc: GarbageCollector, storage: LocalStorage(None), logger: StdErrLogger::new(), - unwinder: Unwinder { unwinding: false }, + unwinder: Unwinder { unwinding: false, cause: None }, taskgroup: None, // FIXME(#7544) make watching optional death: self.death.new_child(), @@ -284,7 +339,7 @@ impl Task { // the unkillable counter is set. This is necessary for when the // taskgroup destruction code drops references on KillHandles, which // might require using unkillable (to synchronize with an unwrapper). - self.death.collect_failure(!self.unwinder.unwinding, self.taskgroup.take()); + self.death.collect_failure(self.unwinder.to_unwind_result(), self.taskgroup.take()); self.destroyed = true; } @@ -469,10 +524,11 @@ impl Unwinder { } } - pub fn begin_unwind(&mut self) -> ! { + pub fn begin_unwind(&mut self, cause: UnwindReason) -> ! { #[fixed_stack_segment]; #[inline(never)]; self.unwinding = true; + self.cause = Some(cause); unsafe { rust_begin_unwind(UNWIND_TOKEN); return transmute(()); @@ -561,55 +617,73 @@ pub extern "C" fn rust_stack_exhausted() { } /// This is the entry point of unwinding for things like lang items and such. -/// The arguments are normally generated by the compiler. +/// The arguments are normally generated by the compiler, and need to +/// have static lifetimes. pub fn begin_unwind(msg: *c_char, file: *c_char, line: size_t) -> ! { + use c_str::CString; + use cast::transmute; + + #[inline] + fn static_char_ptr(p: *c_char) -> &'static str { + let s = unsafe { CString::new(p, false) }; + match s.as_str() { + Some(s) => unsafe { 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_reason(UnwindReasonStr(msg.into_send_str()), file, line as uint) +} + +/// This is the entry point of unwinding for fail!() and assert!(). +pub fn begin_unwind_reason(reason: UnwindReason, file: &'static str, line: uint) -> ! { use rt::in_green_task_context; use rt::task::Task; use rt::local::Local; use str::Str; - use c_str::CString; use unstable::intrinsics; unsafe { - let msg = CString::new(msg, false); - let file = CString::new(file, false); - let msg = match msg.as_str() { - Some(s) => s, None => rtabort!("message wasn't utf8?") - }; + // 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... - if !in_green_task_context() { - 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; + + { + let msg = match reason { + UnwindReasonStr(ref s) => s.as_slice(), + UnwindReasonAny(_) => "~Any", + UnwindReasonLinked => "linked failure", + }; + + if !in_green_task_context() { + rterrln!("failed in non-task context at '{}', {}:{}", + msg, file, line); + intrinsics::abort(); } - intrinsics::abort(); - } - // 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... - let task: *mut Task = Local::unsafe_borrow(); - let n = (*task).name.as_ref().map(|n| n.as_slice()).unwrap_or("<unnamed>"); - - // XXX: this should no get forcibly printed to the console, this should - // either be sent to the parent task (ideally), or get printed to - // the task's logger. Right now the logger is actually a uvio - // instance, which uses unkillable blocks internally for various - // reasons. This will cause serious trouble if the task is failing - // due to mismanagment of its own kill flag, so calling our own - // logger in its current state is a bit of a problem. - match file.as_str() { - Some(file) => { - rterrln!("task '{}' failed at '{}', {}:{}", n, msg, file, line); + task = Local::unsafe_borrow(); + let n = (*task).name.as_ref().map(|n| n.as_slice()).unwrap_or("<unnamed>"); + + // XXX: this should no get forcibly printed to the console, this should + // either be sent to the parent task (ideally), or get printed to + // the task's logger. Right now the logger is actually a uvio + // instance, which uses unkillable blocks internally for various + // reasons. This will cause serious trouble if the task is failing + // due to mismanagment of its own kill flag, so calling our own + // logger in its current state is a bit of a problem. + + rterrln!("task '{}' failed at '{}', {}:{}", n, msg, file, line); + + if (*task).unwinder.unwinding { + rtabort!("unwinding again"); } - None => rterrln!("task '{}' failed at '{}'", n, msg), } - if (*task).unwinder.unwinding { - rtabort!("unwinding again"); - } - (*task).unwinder.begin_unwind(); + + (*task).unwinder.begin_unwind(reason); } } |
