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/task | |
| 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/task')
| -rw-r--r-- | src/libstd/task/mod.rs | 165 | ||||
| -rw-r--r-- | src/libstd/task/spawn.rs | 60 |
2 files changed, 159 insertions, 66 deletions
diff --git a/src/libstd/task/mod.rs b/src/libstd/task/mod.rs index b72d6773ec5..3333b24a924 100644 --- a/src/libstd/task/mod.rs +++ b/src/libstd/task/mod.rs @@ -56,38 +56,68 @@ use prelude::*; use cell::Cell; -use comm::{stream, Chan, GenericChan, GenericPort, Port}; -use result::Result; -use result; +use comm::{stream, Chan, GenericChan, GenericPort, Port, Peekable}; +use result::{Result, Ok, Err}; use rt::in_green_task_context; use rt::local::Local; +use rt::task::{UnwindReasonAny, UnwindReasonLinked, UnwindReasonStr}; +use rt::task::{UnwindResult, Success, Failure}; +use send_str::{SendStr, IntoSendStr}; use unstable::finally::Finally; use util; -use send_str::{SendStr, IntoSendStr}; +#[cfg(test)] use any::Any; #[cfg(test)] use cast; #[cfg(test)] use comm::SharedChan; #[cfg(test)] use comm; #[cfg(test)] use ptr; +#[cfg(test)] use result; #[cfg(test)] use task; pub mod spawn; -/** - * Indicates the manner in which a task exited. - * - * A task that completes without failing is considered to exit successfully. - * Supervised ancestors and linked siblings may yet fail after this task - * succeeds. Also note that in such a case, it may be nondeterministic whether - * linked failure or successful exit happen first. - * - * If you wish for this result's delivery to block until all linked and/or - * children tasks complete, recommend using a result future. - */ -#[deriving(Eq)] -pub enum TaskResult { - Success, - Failure, +/// Indicates the manner in which a task exited. +/// +/// A task that completes without failing is considered to exit successfully. +/// Supervised ancestors and linked siblings may yet fail after this task +/// succeeds. Also note that in such a case, it may be nondeterministic whether +/// linked failure or successful exit happen first. +/// +/// If you wish for this result's delivery to block until all linked and/or +/// children tasks complete, recommend using a result future. +pub type TaskResult = Result<(), ~Any>; + +pub struct LinkedFailure; + +#[inline] +fn wrap_as_any(res: UnwindResult) -> TaskResult { + match res { + Success => Ok(()), + Failure(UnwindReasonStr(s)) => Err(~s as ~Any), + Failure(UnwindReasonAny(a)) => Err(a), + Failure(UnwindReasonLinked) => Err(~LinkedFailure as ~Any) + } +} + +pub struct TaskResultPort { + priv port: Port<UnwindResult> +} + +impl GenericPort<TaskResult> for TaskResultPort { + #[inline] + fn recv(&self) -> TaskResult { + wrap_as_any(self.port.recv()) + } + + #[inline] + fn try_recv(&self) -> Option<TaskResult> { + self.port.try_recv().map(wrap_as_any) + } +} + +impl Peekable<TaskResult> for TaskResultPort { + #[inline] + fn peek(&self) -> bool { self.port.peek() } } /// Scheduler modes @@ -148,7 +178,7 @@ pub struct TaskOpts { priv supervised: bool, priv watched: bool, priv indestructible: bool, - priv notify_chan: Option<Chan<TaskResult>>, + priv notify_chan: Option<Chan<UnwindResult>>, name: Option<SendStr>, sched: SchedOpts, stack_size: Option<uint> @@ -273,7 +303,7 @@ impl TaskBuilder { /// /// # Failure /// Fails if a future_result was already set for this task. - pub fn future_result(&mut self) -> Port<TaskResult> { + pub fn future_result(&mut self) -> TaskResultPort { // FIXME (#3725): Once linked failure and notification are // handled in the library, I can imagine implementing this by just // registering an arbitrary number of task::on_exit handlers and @@ -284,12 +314,12 @@ impl TaskBuilder { } // Construct the future and give it to the caller. - let (notify_pipe_po, notify_pipe_ch) = stream::<TaskResult>(); + let (notify_pipe_po, notify_pipe_ch) = stream::<UnwindResult>(); // Reconfigure self to use a notify channel. self.opts.notify_chan = Some(notify_pipe_ch); - notify_pipe_po + TaskResultPort { port: notify_pipe_po } } /// Name the task-to-be. Currently the name is used for identification @@ -394,7 +424,7 @@ impl TaskBuilder { * # Failure * Fails if a future_result was already set for this task. */ - pub fn try<T:Send>(&mut self, f: ~fn() -> T) -> Result<T,()> { + pub fn try<T:Send>(&mut self, f: ~fn() -> T) -> Result<T, ~Any> { let (po, ch) = stream::<T>(); let result = self.future_result(); @@ -404,8 +434,8 @@ impl TaskBuilder { } match result.recv() { - Success => result::Ok(po.recv()), - Failure => result::Err(()) + Ok(()) => Ok(po.recv()), + Err(cause) => Err(cause) } } } @@ -512,7 +542,7 @@ pub fn spawn_sched(mode: SchedMode, f: ~fn()) { task.spawn(f) } -pub fn try<T:Send>(f: ~fn() -> T) -> Result<T,()> { +pub fn try<T:Send>(f: ~fn() -> T) -> Result<T, ~Any> { /*! * Execute a function in another task and return either the return value * of the function or result::err. @@ -769,7 +799,7 @@ fn test_spawn_unlinked_sup_no_fail_up() { // child unlinked fails fn test_spawn_unlinked_sup_fail_down() { use rt::test::run_in_uv_task; do run_in_uv_task { - let result: Result<(),()> = do try { + let result: Result<(), ~Any> = do try { do spawn_supervised { block_forever(); } fail!(); // Shouldn't leave a child hanging around. }; @@ -782,7 +812,7 @@ fn test_spawn_unlinked_sup_fail_down() { fn test_spawn_linked_sup_fail_up() { // child fails; parent fails use rt::test::run_in_uv_task; do run_in_uv_task { - let result: Result<(),()> = do try { + let result: Result<(), ~Any> = do try { // Unidirectional "parenting" shouldn't override bidirectional linked. // We have to cheat with opts - the interface doesn't support them because // they don't make sense (redundant with task().supervised()). @@ -803,7 +833,7 @@ fn test_spawn_linked_sup_fail_up() { // child fails; parent fails fn test_spawn_linked_sup_fail_down() { // parent fails; child fails use rt::test::run_in_uv_task; do run_in_uv_task { - let result: Result<(),()> = do try { + let result: Result<(), ~Any> = do try { // We have to cheat with opts - the interface doesn't support them because // they don't make sense (redundant with task().supervised()). let mut b0 = task(); @@ -820,7 +850,7 @@ fn test_spawn_linked_sup_fail_down() { // parent fails; child fails fn test_spawn_linked_unsup_fail_up() { // child fails; parent fails use rt::test::run_in_uv_task; do run_in_uv_task { - let result: Result<(),()> = do try { + let result: Result<(), ~Any> = do try { // Default options are to spawn linked & unsupervised. do spawn { fail!(); } block_forever(); // We should get punted awake @@ -833,7 +863,7 @@ fn test_spawn_linked_unsup_fail_up() { // child fails; parent fails fn test_spawn_linked_unsup_fail_down() { // parent fails; child fails use rt::test::run_in_uv_task; do run_in_uv_task { - let result: Result<(),()> = do try { + let result: Result<(), ~Any> = do try { // Default options are to spawn linked & unsupervised. do spawn { block_forever(); } fail!(); @@ -846,7 +876,7 @@ fn test_spawn_linked_unsup_fail_down() { // parent fails; child fails fn test_spawn_linked_unsup_default_opts() { // parent fails; child fails use rt::test::run_in_uv_task; do run_in_uv_task { - let result: Result<(),()> = do try { + let result: Result<(), ~Any> = do try { // Make sure the above test is the same as this one. let mut builder = task(); builder.linked(); @@ -865,7 +895,7 @@ fn test_spawn_linked_unsup_default_opts() { // parent fails; child fails fn test_spawn_failure_propagate_grandchild() { use rt::test::run_in_uv_task; do run_in_uv_task { - let result: Result<(),()> = do try { + let result: Result<(), ~Any> = do try { // Middle task exits; does grandparent's failure propagate across the gap? do spawn_supervised { do spawn_supervised { block_forever(); } @@ -882,7 +912,7 @@ fn test_spawn_failure_propagate_grandchild() { fn test_spawn_failure_propagate_secondborn() { use rt::test::run_in_uv_task; do run_in_uv_task { - let result: Result<(),()> = do try { + let result: Result<(), ~Any> = do try { // First-born child exits; does parent's failure propagate to sibling? do spawn_supervised { do spawn { block_forever(); } // linked @@ -899,7 +929,7 @@ fn test_spawn_failure_propagate_secondborn() { fn test_spawn_failure_propagate_nephew_or_niece() { use rt::test::run_in_uv_task; do run_in_uv_task { - let result: Result<(),()> = do try { + let result: Result<(), ~Any> = do try { // Our sibling exits; does our failure propagate to sibling's child? do spawn { // linked do spawn_supervised { block_forever(); } @@ -916,7 +946,7 @@ fn test_spawn_failure_propagate_nephew_or_niece() { fn test_spawn_linked_sup_propagate_sibling() { use rt::test::run_in_uv_task; do run_in_uv_task { - let result: Result<(),()> = do try { + let result: Result<(), ~Any> = do try { // Middle sibling exits - does eldest's failure propagate to youngest? do spawn { // linked do spawn { block_forever(); } // linked @@ -1024,7 +1054,7 @@ fn test_future_result() { let mut builder = task(); let result = builder.future_result(); do builder.spawn {} - assert_eq!(result.recv(), Success); + assert!(result.recv().is_ok()); let mut builder = task(); let result = builder.future_result(); @@ -1032,7 +1062,7 @@ fn test_future_result() { do builder.spawn { fail!(); } - assert_eq!(result.recv(), Failure); + assert!(result.recv().is_err()); } #[test] #[should_fail] @@ -1057,7 +1087,7 @@ fn test_try_fail() { match do try { fail!() } { - result::Err(()) => (), + result::Err(_) => (), result::Ok(()) => fail!() } } @@ -1393,3 +1423,58 @@ fn test_indestructible() { assert!(result.is_ok()); } } + +#[test] +fn test_try_fail_cause_static_str() { + match do try { + fail!("static string"); + } { + Err(ref e) if e.is::<SendStr>() => {} + Err(_) | Ok(()) => fail!() + } +} + +#[test] +fn test_try_fail_cause_owned_str() { + match do try { + fail!(~"owned string"); + } { + Err(ref e) if e.is::<SendStr>() => {} + Err(_) | Ok(()) => fail!() + } +} + +#[test] +fn test_try_fail_cause_any() { + match do try { + fail!(~413u16 as ~Any); + } { + Err(ref e) if e.is::<u16>() => {} + Err(_) | Ok(()) => fail!() + } +} + +#[ignore(reason = "linked failure")] +#[test] +fn test_try_fail_cause_linked() { + match do try { + do spawn { + fail!() + } + } { + Err(ref e) if e.is::<LinkedFailure>() => {} + Err(_) | Ok(()) => fail!() + } +} + +#[test] +fn test_try_fail_cause_any_wrapped() { + struct Juju; + + match do try { + fail!(~Juju) + } { + Err(ref e) if e.is::<Juju>() => {} + Err(_) | Ok(()) => fail!() + } +} diff --git a/src/libstd/task/spawn.rs b/src/libstd/task/spawn.rs index fbe2988f77c..235e67048f6 100644 --- a/src/libstd/task/spawn.rs +++ b/src/libstd/task/spawn.rs @@ -76,21 +76,24 @@ use prelude::*; use cast::transmute; use cast; use cell::Cell; -use container::MutableMap; use comm::{Chan, GenericChan, oneshot}; +use container::MutableMap; use hashmap::{HashSet, HashSetMoveIterator}; use local_data; -use task::{Failure, SingleThreaded}; -use task::{Success, TaskOpts, TaskResult}; -use task::unkillable; -use uint; -use util; -use unstable::sync::Exclusive; use rt::in_green_task_context; use rt::local::Local; -use rt::task::{Task, Sched}; use rt::shouldnt_be_public::{Scheduler, KillHandle, WorkQueue, Thread, EventLoop}; +use rt::task::{Task, Sched}; +use rt::task::{UnwindReasonLinked, UnwindReasonStr}; +use rt::task::{UnwindResult, Success, Failure}; use rt::uv::uvio::UvEventLoop; +use send_str::IntoSendStr; +use task::SingleThreaded; +use task::TaskOpts; +use task::unkillable; +use uint; +use unstable::sync::Exclusive; +use util; #[cfg(test)] use task::default_task_opts; #[cfg(test)] use comm; @@ -321,7 +324,7 @@ impl Drop for Taskgroup { do RuntimeGlue::with_task_handle_and_failing |me, failing| { if failing { for x in self.notifier.mut_iter() { - x.failed = true; + x.task_result = Some(Failure(UnwindReasonLinked)); } // Take everybody down with us. After this point, every // other task in the group will see 'tg' as none, which @@ -353,7 +356,7 @@ pub fn Taskgroup(tasks: TaskGroupArc, ancestors: AncestorList, mut notifier: Option<AutoNotify>) -> Taskgroup { for x in notifier.mut_iter() { - x.failed = false; + x.task_result = Some(Success); } Taskgroup { @@ -364,21 +367,28 @@ pub fn Taskgroup(tasks: TaskGroupArc, } struct AutoNotify { - notify_chan: Chan<TaskResult>, - failed: bool, + notify_chan: Chan<UnwindResult>, + + // XXX: By value self drop would allow this to be a plain UnwindResult + task_result: Option<UnwindResult>, } -impl Drop for AutoNotify { - fn drop(&mut self) { - let result = if self.failed { Failure } else { Success }; - self.notify_chan.send(result); +impl AutoNotify { + pub fn new(chan: Chan<UnwindResult>) -> AutoNotify { + AutoNotify { + notify_chan: chan, + + // Un-set above when taskgroup successfully made. + task_result: Some(Failure(UnwindReasonStr("AutoNotify::new()".into_send_str()))) + } } } -fn AutoNotify(chan: Chan<TaskResult>) -> AutoNotify { - AutoNotify { - notify_chan: chan, - failed: true // Un-set above when taskgroup successfully made. +impl Drop for AutoNotify { + fn drop(&mut self) { + let result = self.task_result.take_unwrap(); + + self.notify_chan.send(result); } } @@ -675,10 +685,8 @@ pub fn spawn_raw(mut opts: TaskOpts, f: ~fn()) { if opts.notify_chan.is_some() { let notify_chan = opts.notify_chan.take_unwrap(); let notify_chan = Cell::new(notify_chan); - let on_exit: ~fn(bool) = |success| { - notify_chan.take().send( - if success { Success } else { Failure } - ) + let on_exit: ~fn(UnwindResult) = |task_result| { + notify_chan.take().send(task_result) }; task.death.on_exit = Some(on_exit); } @@ -721,7 +729,7 @@ fn test_spawn_raw_notify_success() { }; do spawn_raw(opts) { } - assert_eq!(notify_po.recv(), Success); + assert!(notify_po.recv().is_success()); } #[test] @@ -738,5 +746,5 @@ fn test_spawn_raw_notify_failure() { do spawn_raw(opts) { fail!(); } - assert_eq!(notify_po.recv(), Failure); + assert!(notify_po.recv().is_failure()); } |
