diff options
| author | Marvin Löbel <loebel.marvin@gmail.com> | 2013-10-11 23:20:34 +0200 |
|---|---|---|
| committer | Marvin Löbel <loebel.marvin@gmail.com> | 2013-10-28 08:50:32 +0100 |
| commit | fa8e71a8257f4226ab532d4bf268d3ecbfa98eb4 (patch) | |
| tree | 0b8051814dd8a5ef08e663c172e2b456065d625d /src/libstd/rt/task.rs | |
| parent | cb5b21eba713ff3888b2741db4c9e7d841cfde02 (diff) | |
| download | rust-fa8e71a8257f4226ab532d4bf268d3ecbfa98eb4.tar.gz rust-fa8e71a8257f4226ab532d4bf268d3ecbfa98eb4.zip | |
Allow fail messages to be caught, and introduce the Any trait
Some code cleanup, sorting of import blocks Removed std::unstable::UnsafeArc's use of Either Added run-fail tests for the new FailWithCause impls Changed future_result and try to return Result<(), ~Any>. - Internally, there is an enum of possible fail messages passend around. - In case of linked failure or a string message, the ~Any gets lazyly allocated in future_results recv method. - For that, future result now returns a wrapper around a Port. - Moved and renamed task::TaskResult into rt::task::UnwindResult and made it an internal enum. - Introduced a replacement typedef `type TaskResult = Result<(), ~Any>`.
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); } } |
