Skip to content

Commit 06e93c2

Browse files
committedDec 8, 2015
Auto merge of #29937 - alexcrichton:panic-recover, r=aturon
This commit is an implementation of [RFC 1236] and [RFC 1323] which rename the `thread::catch_panic` function to `panic::recover` while also replacing the `Send + 'static` bounds with a new `PanicSafe` bound. [RFC 1236]: rust-lang/rfcs#1236 [RFC 1323]: rust-lang/rfcs#1323 cc #27719
2 parents acf4e0b + c39af12 commit 06e93c2

13 files changed

+458
-45
lines changed
 

‎src/libstd/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -248,13 +248,15 @@
248248
#![feature(link_args)]
249249
#![feature(linkage)]
250250
#![feature(macro_reexport)]
251+
#![feature(on_unimplemented)]
251252
#![feature(oom)]
252253
#![feature(optin_builtin_traits)]
253254
#![feature(placement_in_syntax)]
254255
#![feature(rand)]
255256
#![feature(range_inclusive)]
256257
#![feature(raw)]
257258
#![feature(reflect_marker)]
259+
#![feature(shared)]
258260
#![feature(slice_bytes)]
259261
#![feature(slice_concat_ext)]
260262
#![feature(slice_patterns)]
@@ -424,6 +426,7 @@ pub mod fs;
424426
pub mod io;
425427
pub mod net;
426428
pub mod os;
429+
pub mod panic;
427430
pub mod path;
428431
pub mod process;
429432
pub mod sync;

‎src/libstd/panic.rs

+255
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
//! Panic support in the standard library
12+
13+
#![unstable(feature = "std_panic", reason = "awaiting feedback",
14+
issue = "27719")]
15+
16+
use cell::UnsafeCell;
17+
use ops::{Deref, DerefMut};
18+
use ptr::{Unique, Shared};
19+
use rc::Rc;
20+
use sync::{Arc, Mutex, RwLock};
21+
use sys_common::unwind;
22+
use thread::Result;
23+
24+
/// A marker trait which represents "panic safe" types in Rust.
25+
///
26+
/// This trait is implemented by default for many types and behaves similarly in
27+
/// terms of inference of implementation to the `Send` and `Sync` traits. The
28+
/// purpose of this trait is to encode what types are safe to cross a `recover`
29+
/// boundary with no fear of panic safety.
30+
///
31+
/// ## What is panic safety?
32+
///
33+
/// In Rust a function can "return" early if it either panics or calls a
34+
/// function which transitively panics. This sort of control flow is not always
35+
/// anticipated, and has the possibility of causing subtle bugs through a
36+
/// combination of two cricial components:
37+
///
38+
/// 1. A data structure is in a temporarily invalid state when the thread
39+
/// panics.
40+
/// 2. This broken invariant is then later observed.
41+
///
42+
/// Typically in Rust it is difficult to perform step (2) because catching a
43+
/// panic involves either spawning a thread (which in turns makes it difficult
44+
/// to later witness broken invariants) or using the `recover` function in this
45+
/// module. Additionally, even if an invariant is witness, it typically isn't a
46+
/// problem in Rust because there's no uninitialized values (like in C or C++).
47+
///
48+
/// It is possible, however, for **logical** invariants to be broken in Rust,
49+
/// which can end up causing behavioral bugs. Another key aspect of panic safety
50+
/// in Rust is that in the absence of `unsafe` code, a panic cannot lead to
51+
/// memory unsafety.
52+
///
53+
/// That was a bit of a whirlwind tour of panic safety, but for more information
54+
/// about panic safety and how it applies to Rust, see an [associated RFC][rfc].
55+
///
56+
/// [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1236-stabilize-catch-panic.md
57+
///
58+
/// ## What is `RecoverSafe`?
59+
///
60+
/// Now that we've got an idea of what panic safety is in Rust, it's also
61+
/// important to understand that this trait represents. As mentioned above, one
62+
/// way to witness broken invariants is through the `recover` function in this
63+
/// module as it allows catching a panic and then re-using the environment of
64+
/// the closure.
65+
///
66+
/// Simply but, a type `T` implements `RecoverSafe` if it cannot easily allow
67+
/// witnessing a broken invariant through the use of `recover` (catching a
68+
/// panic). This trait is a marker trait, so it is automatically implemented for
69+
/// many types, and it is also structurally composed (e.g. a struct is recover
70+
/// safe if all of its components are recover safe).
71+
///
72+
/// Note, however, that this is not an unsafe trait, so there is not a succinct
73+
/// contract that this trait is providing. Instead it is intended as more of a
74+
/// "speed bump" to alert users of `recover` that broken invariants may be
75+
/// witnessed and may need to be accounted for.
76+
///
77+
/// ## Who implements `RecoverSafe`?
78+
///
79+
/// Types such as `&mut T` and `&RefCell<T>` are examples which are **not**
80+
/// recover safe. The general idea is that any mutable state which can be shared
81+
/// across `recover` is not recover safe by default. This is because it is very
82+
/// easy to witness a broken invariant outside of `recover` as the data is
83+
/// simply accesed as usual.
84+
///
85+
/// Types like `&Mutex<T>`, however, are recover safe because they implement
86+
/// poisoning by default. They still allow witnessing a broken invariant, but
87+
/// they already provide their own "speed bumps" to do so.
88+
///
89+
/// ## When should `RecoverSafe` be used?
90+
///
91+
/// Is not intended that most types or functions need to worry about this trait.
92+
/// It is only used as a bound on the `recover` function and as mentioned above,
93+
/// the lack of `unsafe` means it is mostly an advisory. The `AssertRecoverSafe`
94+
/// wrapper struct in this module can be used to force this trait to be
95+
/// implemented for any closed over variables passed to the `recover` function
96+
/// (more on this below).
97+
#[unstable(feature = "recover", reason = "awaiting feedback", issue = "27719")]
98+
#[rustc_on_unimplemented = "the type {Self} may not be safely transferred \
99+
across a recover boundary"]
100+
pub trait RecoverSafe {}
101+
102+
/// A marker trait representing types which do not contain an `UnsafeCell` by
103+
/// value internally.
104+
///
105+
/// This is a "helper marker trait" used to provide impl blocks for the
106+
/// `RecoverSafe` trait, for more information see that documentation.
107+
#[unstable(feature = "recover", reason = "awaiting feedback", issue = "27719")]
108+
#[rustc_on_unimplemented = "the type {Self} contains interior mutability \
109+
and a reference may not be safely transferrable \
110+
across a recover boundary"]
111+
pub trait NoUnsafeCell {}
112+
113+
/// A simple wrapper around a type to assert that it is panic safe.
114+
///
115+
/// When using `recover` it may be the case that some of the closed over
116+
/// variables are not panic safe. For example if `&mut T` is captured the
117+
/// compiler will generate a warning indicating that it is not panic safe. It
118+
/// may not be the case, however, that this is actually a problem due to the
119+
/// specific usage of `recover` if panic safety is specifically taken into
120+
/// account. This wrapper struct is useful for a quick and lightweight
121+
/// annotation that a variable is indeed panic safe.
122+
///
123+
/// # Examples
124+
///
125+
/// ```
126+
/// #![feature(recover, std_panic)]
127+
///
128+
/// use std::panic::{self, AssertRecoverSafe};
129+
///
130+
/// let mut variable = 4;
131+
///
132+
/// // This code will not compile becuause the closure captures `&mut variable`
133+
/// // which is not considered panic safe by default.
134+
///
135+
/// // panic::recover(|| {
136+
/// // variable += 3;
137+
/// // });
138+
///
139+
/// // This, however, will compile due to the `AssertRecoverSafe` wrapper
140+
/// let result = {
141+
/// let mut wrapper = AssertRecoverSafe::new(&mut variable);
142+
/// panic::recover(move || {
143+
/// **wrapper += 3;
144+
/// })
145+
/// };
146+
/// // ...
147+
/// ```
148+
#[unstable(feature = "recover", reason = "awaiting feedback", issue = "27719")]
149+
pub struct AssertRecoverSafe<T>(T);
150+
151+
// Implementations of the `RecoverSafe` trait:
152+
//
153+
// * By default everything is recover safe
154+
// * pointers T contains mutability of some form are not recover safe
155+
// * Unique, an owning pointer, lifts an implementation
156+
// * Types like Mutex/RwLock which are explicilty poisoned are recover safe
157+
// * Our custom AssertRecoverSafe wrapper is indeed recover safe
158+
impl RecoverSafe for .. {}
159+
impl<'a, T: ?Sized> !RecoverSafe for &'a mut T {}
160+
impl<'a, T: NoUnsafeCell + ?Sized> RecoverSafe for &'a T {}
161+
impl<T: NoUnsafeCell + ?Sized> RecoverSafe for *const T {}
162+
impl<T: NoUnsafeCell + ?Sized> RecoverSafe for *mut T {}
163+
impl<T: RecoverSafe> RecoverSafe for Unique<T> {}
164+
impl<T: NoUnsafeCell + ?Sized> RecoverSafe for Shared<T> {}
165+
impl<T: ?Sized> RecoverSafe for Mutex<T> {}
166+
impl<T: ?Sized> RecoverSafe for RwLock<T> {}
167+
impl<T> RecoverSafe for AssertRecoverSafe<T> {}
168+
169+
// not covered via the Shared impl above b/c the inner contents use
170+
// Cell/AtomicUsize, but the usage here is recover safe so we can lift the
171+
// impl up one level to Arc/Rc itself
172+
impl<T: NoUnsafeCell + ?Sized> RecoverSafe for Rc<T> {}
173+
impl<T: NoUnsafeCell + ?Sized> RecoverSafe for Arc<T> {}
174+
175+
// Pretty simple implementations for the `NoUnsafeCell` marker trait, basically
176+
// just saying that this is a marker trait and `UnsafeCell` is the only thing
177+
// which doesn't implement it (which then transitively applies to everything
178+
// else.
179+
impl NoUnsafeCell for .. {}
180+
impl<T: ?Sized> !NoUnsafeCell for UnsafeCell<T> {}
181+
182+
impl<T> AssertRecoverSafe<T> {
183+
/// Creates a new `AssertRecoverSafe` wrapper around the provided type.
184+
#[unstable(feature = "recover", reason = "awaiting feedback", issue = "27719")]
185+
pub fn new(t: T) -> AssertRecoverSafe<T> {
186+
AssertRecoverSafe(t)
187+
}
188+
}
189+
190+
impl<T> Deref for AssertRecoverSafe<T> {
191+
type Target = T;
192+
193+
fn deref(&self) -> &T {
194+
&self.0
195+
}
196+
}
197+
198+
impl<T> DerefMut for AssertRecoverSafe<T> {
199+
fn deref_mut(&mut self) -> &mut T {
200+
&mut self.0
201+
}
202+
}
203+
204+
/// Invokes a closure, capturing the cause of panic if one occurs.
205+
///
206+
/// This function will return `Ok` with the closure's result if the closure
207+
/// does not panic, and will return `Err(cause)` if the closure panics. The
208+
/// `cause` returned is the object with which panic was originally invoked.
209+
///
210+
/// It is currently undefined behavior to unwind from Rust code into foreign
211+
/// code, so this function is particularly useful when Rust is called from
212+
/// another language (normally C). This can run arbitrary Rust code, capturing a
213+
/// panic and allowing a graceful handling of the error.
214+
///
215+
/// It is **not** recommended to use this function for a general try/catch
216+
/// mechanism. The `Result` type is more appropriate to use for functions that
217+
/// can fail on a regular basis.
218+
///
219+
/// The closure provided is required to adhere to the `RecoverSafe` to ensure
220+
/// that all captured variables are safe to cross this recover boundary. The
221+
/// purpose of this bound is to encode the concept of [exception safety][rfc] in
222+
/// the type system. Most usage of this function should not need to worry about
223+
/// this bound as programs are naturally panic safe without `unsafe` code. If it
224+
/// becomes a problem the associated `AssertRecoverSafe` wrapper type in this
225+
/// module can be used to quickly assert that the usage here is indeed exception
226+
/// safe.
227+
///
228+
/// [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1236-stabilize-catch-panic.md
229+
///
230+
/// # Examples
231+
///
232+
/// ```
233+
/// #![feature(recover, std_panic)]
234+
///
235+
/// use std::panic;
236+
///
237+
/// let result = panic::recover(|| {
238+
/// println!("hello!");
239+
/// });
240+
/// assert!(result.is_ok());
241+
///
242+
/// let result = panic::recover(|| {
243+
/// panic!("oh no!");
244+
/// });
245+
/// assert!(result.is_err());
246+
/// ```
247+
#[unstable(feature = "recover", reason = "awaiting feedback", issue = "27719")]
248+
pub fn recover<F: FnOnce() -> R + RecoverSafe, R>(f: F) -> Result<R> {
249+
let mut result = None;
250+
unsafe {
251+
let result = &mut result;
252+
try!(unwind::try(move || *result = Some(f())))
253+
}
254+
Ok(result.unwrap())
255+
}

‎src/libstd/rt.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,11 @@
2424

2525
use borrow::ToOwned;
2626
use mem;
27+
use panic;
2728
use sys;
2829
use sys_common::thread_info::{self, NewThread};
2930
use sys_common;
30-
use thread::{self, Thread};
31+
use thread::Thread;
3132

3233
// Reexport some of our utilities which are expected by other crates.
3334
pub use sys_common::unwind::{begin_unwind, begin_unwind_fmt};
@@ -57,7 +58,7 @@ fn lang_start(main: *const u8, argc: isize, argv: *const *const u8) -> isize {
5758
sys_common::args::init(argc, argv);
5859

5960
// Let's run some code!
60-
let res = thread::catch_panic(mem::transmute::<_, fn()>(main));
61+
let res = panic::recover(mem::transmute::<_, fn()>(main));
6162
sys_common::cleanup();
6263
res.is_err()
6364
};

‎src/libstd/thread/mod.rs

+1-18
Original file line numberDiff line numberDiff line change
@@ -355,26 +355,9 @@ pub fn panicking() -> bool {
355355
/// with exception safety. Furthermore, a `Send` bound is also required,
356356
/// providing the same safety guarantees as `thread::spawn` (ensuring the
357357
/// closure is properly isolated from the parent).
358-
///
359-
/// # Examples
360-
///
361-
/// ```
362-
/// #![feature(catch_panic)]
363-
///
364-
/// use std::thread;
365-
///
366-
/// let result = thread::catch_panic(|| {
367-
/// println!("hello!");
368-
/// });
369-
/// assert!(result.is_ok());
370-
///
371-
/// let result = thread::catch_panic(|| {
372-
/// panic!("oh no!");
373-
/// });
374-
/// assert!(result.is_err());
375-
/// ```
376358
#[unstable(feature = "catch_panic", reason = "recent API addition",
377359
issue = "27719")]
360+
#[rustc_deprecated(since = "1.6.0", reason = "renamed to std::panic::recover")]
378361
pub fn catch_panic<F, R>(f: F) -> Result<R>
379362
where F: FnOnce() -> R + Send + 'static
380363
{
+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![allow(dead_code)]
12+
#![feature(recover)]
13+
14+
use std::panic::RecoverSafe;
15+
use std::rc::Rc;
16+
17+
fn assert<T: RecoverSafe + ?Sized>() {}
18+
19+
fn main() {
20+
assert::<Rc<RefCell<i32>>>(); //~ ERROR: is not implemented
21+
}
22+
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![allow(dead_code)]
12+
#![feature(recover)]
13+
14+
use std::panic::RecoverSafe;
15+
use std::sync::Arc;
16+
17+
fn assert<T: RecoverSafe + ?Sized>() {}
18+
19+
fn main() {
20+
assert::<Arc<RefCell<i32>>>(); //~ ERROR: is not implemented
21+
}
+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![allow(dead_code)]
12+
#![feature(recover)]
13+
14+
use std::panic::RecoverSafe;
15+
use std::cell::RefCell;
16+
17+
fn assert<T: RecoverSafe + ?Sized>() {}
18+
19+
fn main() {
20+
assert::<&RefCell<i32>>(); //~ ERROR: is not implemented
21+
//~^ ERROR is not implemented
22+
}

0 commit comments

Comments
 (0)
Please sign in to comment.