Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tracking issue for recover stabilization #27719

Closed
aturon opened this issue Aug 12, 2015 · 96 comments
Closed

Tracking issue for recover stabilization #27719

aturon opened this issue Aug 12, 2015 · 96 comments
Labels
B-unstable Blocker: Implemented in the nightly compiler and unstable. final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.

Comments

@aturon
Copy link
Member

aturon commented Aug 12, 2015

The recover function is able, on a single thread, to recover from a panic. There are numerous controversies around this API, including the bounds it applies to its closure, its safety, and its location within std. An open RFC seeks to settle these issues and open the door to stabilization.

cc @alexcrichton

@aturon aturon added T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. B-unstable Blocker: Implemented in the nightly compiler and unstable. labels Aug 12, 2015
alexcrichton added a commit to alexcrichton/rust that referenced this issue Aug 31, 2015
This commit is an implementation of [RFC 1236][rfc] which renames the
`thread::catch_panic` function to `panic::recover` while also removing the
`Send` bound (but leaving the `'static` bound).

[rfc]: rust-lang/rfcs#1236

cc rust-lang#27719
@SimonSapin
Copy link
Contributor

I’m playing with making Python bindings for a Rust library, and I got some segfaults when a Rust panic tried to unwind into the Python interpreter’s call stack. I understand that using catch_panic at the FFI boundary is the right thing to do, but what should I do with an Err result? I can establish conventions so that the other side triggers a Python exception, but I’d like to have some more helpful information in it than "Something bad happened somewhere". At least (a string representation of) the message that was given to panic!, ideally also a back trace of the unwinded stack frames.

catch_panic returns Result<T, Box<Any + Send + 'static>>, but Any is not very helpful here. It implements Debug, but that impl only prints "Any" (literally), which isn’t that helpful.

@wycats, how do you deal with panics in Rust-inside-Ruby?

@abonander
Copy link
Contributor

@SimonSapin Looking at the guts of unwinding, Box<Any + Send + 'static> is always going to either be String (if panic!() had formatting arguments) or &'static str (if panic!() was invoked with just a string literal), so you basically downcast it to one, and if that fails you can probably assume it's the other.

I agree, though, the type could be a lot more informative here.

@SimonSapin
Copy link
Contributor

Yeah, I ended up finding this code: https://github.com/rust-lang/rust/blob/d2047bc97d/src/libstd/panicking.rs#L33-L39 , but it’s not very discoverable. And though String and &'static str are the most common cases, panic!() with a single argument can take any type:

SimonSapin: panic!(4)
playbot: thread '<main>' panicked at 'Box<Any>', <anon>:9
playbot: playpen: application terminated with error code 101

So I wanted to propose changing from this (in std::thread):

type Result<T> = Result<T, Box<Any + Send + 'static>>;

to this:

type Result<T> = Result<T, PanicValue>;
struct PanicValue(Box<Any + Send + 'static>);
impl PanicValue {
    fn as_str(&self) -> Option<&str> {
        match self.0.downcast_ref::<&'static str>() {
            Some(s) => Some(*s),
            None => match self.0.downcast_ref::<String>() {
                Some(s) => Some(&s[..]),
                None => None,
            }
        }
    }
}

… but std::thread::Result is already stable :(

@SimonSapin
Copy link
Contributor

Would this as_str belong as a default method on the Any trait?

@abonander
Copy link
Contributor

Why not have impl Debug for Any (+ Send) test for the value being String or &'static str before printing "Any"? It would add negligible overhead, and I don't think anyone should be relying on the exact value it prints. Right now, it's pretty much useless anyways.

In fact, why not cover all the primitives (except obviously &[T], *const T, *mut T, etc)?

@alexchandel
Copy link

Why are we making it easier to use and abuse panics, instead of removing inappropriate ones from the standard library?

@SimonSapin
Copy link
Contributor

@alexchandel Even if you don’t choose to use panics, it’s very hard to be 100% sure that a non-trivial program will not panic ever. (Similar to being 100% sure that it doesn’t have any bug.) And since unwinding through an FFI boundary can be undefined behavior, something like catch_panic is necessary at least to protect against that.

@steveklabnik
Copy link
Member

Why are we making it easier to use and abuse panics,

There is a whole "Motivations" section of the related RFC which describes this https://github.com/alexcrichton/rfcs/blob/stabilize-catch-panic/text/0000-stabilize-catch-panic.md#motivation

alexcrichton added a commit to alexcrichton/rust that referenced this issue Dec 8, 2015
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 rust-lang#27719
bors added a commit that referenced this issue Dec 8, 2015
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
alexcrichton added a commit to alexcrichton/rust that referenced this issue Dec 9, 2015
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 rust-lang#27719
bors added a commit that referenced this issue Dec 9, 2015
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
@diwic
Copy link
Contributor

diwic commented Mar 13, 2016

Naming

I would refrain from using "unwind": It's not currently exposed in the stdlib, and also, it could mean "to relax", which is the opposite of panicking, really :-) And if "catch" is undesirable (I don't remember exactly why) then maybe std::panic::capture could be an option? And std::panic::release to re-throw the panic. E g, consider a herd of panicking animals, which you capture in a cage, move them safely over some boundary, and then release them so they panic wildly again. RecoverSafe then becomes either std::panic::CaptureSafe or possibly just std::panic::Safe.

PanicInfo

The panic handlers added the PanicInfo struct, and recover/propagate has been some kind of parallel process. It would be nice if this API could be a little more consistent, so that recover would actually return a panicinfo somehow, and propagate allowed to take one as a parameter. I'm not exactly sure if this means redesigning PanicInfo, so I'm just going to leave that as a thought for now.

Landing pads for extern fn

I was wondering if we need an abort-on-panic landing pad on extern fns. Even with this interface stablized, the code is going to end up like:

extern fn callback_from_c( /* ... */ ) -> /* ... */ {
    let r = std::panic::capture(|| /* some complicated handling */ );
    if r.is_err() { /* convert to some sort of error */ }
}

...it's going to be hard to guarantee to 100% that the "convert to some sort of error" part is never going to panic. Since we don't want UB, maybe we need to insert an abort-on-panic landing pad as part of all extern fn?

@nikomatsakis
Copy link
Contributor

On Sat, Mar 12, 2016 at 12:13:56PM -0800, wthrowe wrote:

My concern with the current bounds is still less with anything about the API itself than with the fact that this changes the rather strange nature of rustc's OIBIT handling (#27554) from something that was probably rarely encountered in practice to something exposed in a stable API in the standard library.

I think the most prominent use of auto traits (aka OIBIT) is Send
and Sync, not this relatively obscure function.

This is going to make changing the OIBIT semantics without breaking the world really hard, and I don't think enough thought has been put into the current form to make locking the language into it desirable.

I am not sure what changes you are describing (there are none in the
works that I am aware of, just clarifications -- most of which in fact
behave the same as the current implementation), but as I said, I would
expect the real problem for compatibility with any change is going to
be Send and Sync!

@bstrie
Copy link
Contributor

bstrie commented Mar 21, 2016

I'm happy to see the progress here, and just as happy that we've managed to smooth over the ergonomics without throwing out the bounds entirely.

Unsurprisingly, I'm +1 to a more descriptive name than "recover". This topic comes up so much in the wild that we're going to be perpetually fielding responses to questions like "why does Rust now have exceptions?" and having self-explanatory naming would make that so much less tedious.

To reiterate:

  1. "recover" isn't good because recovery isn't really what's happening here, and we especially don't want people to think that we intend recovery to be what this is used for.
  2. "catch" is even worse because of the conflation with exceptions mentioned above. This mechanism is not intended for the same task as an exception handler is, and we overwhelmingly resist the notion that it should be used that way. This isn't as catastrophically bad of a name as it used to be now that the bounds are remaining, but it's still going to make my job much harder (and that includes variants like catch_unwind).
  3. Name should contain "unwind" because that's presumably the accepted terminology that we'll be using in manual and guides referring to this feature, as well as in documents that discuss panics in general.

I like @nikomatsakis 's halt_unwind and @alexcrichton 's prevent_unwind out of the options presented so far, and @Amanieu 's AssertUnwindSafe.

@aturon
Copy link
Member Author

aturon commented Mar 24, 2016

@pnkfelix semi-jokingly proposes recoil :)

@glaebhoerl
Copy link
Contributor

what about "intercept"?

twittner added a commit to wireapp/cryptobox-c that referenced this issue Mar 29, 2016
`catch_panic` is no longer available in rust nightly. Instead `recover`
has been proposed [1] and is available behind a feature gate in nightly,
beta and stable [2]. This commit transitions from `catch_panic` to `panic`.

---
[1] rust-lang/rust#27719
[2] http://doc.rust-lang.org/std/panic/fn.recover.html
@abonander
Copy link
Contributor

Looks like the discussion is still open for a name so I'll throw out a few ideas. This thread's a little long and these are somewhat generic so it's a little too tedious this late at night to Ctrl-F to see if any of them have already been mentioned, so bear with me.

recover->

  • guard (like guard_from_unwinding but not intentionally unwieldy)
  • barrier (panic::barrier is pretty descriptive, methinks)
  • contain (like containing a panicked crowd or herd of animals)
  • sequester (if you're feeling pretentious)

propagate->

  • Keep it? It's pretty self-explanatory.
  • release (I know this one's already been mentioned but it parallels well with contain)
  • resume (heavily implies continuation of a previously caught panic, which I think is generally desired)

I kind of agree with the abort landing pads in extern fn, though it should be trivial to disable them so the people who do their homework and use recover (or what-have-you) properly don't have pay for them. I'm thinking a function attribute, like #[recover_safe] so that it's right next to documentation explaining how panics are handled properly. Or maybe, like integer overflow traps, they should only be emitted in debug builds. It could also be a lint that warns whenever possibly fallible operations are undertaken, namely calling into non-extern functions and methods, but only once per extern fn.

@alexcrichton
Copy link
Member

So far the front-runner in terms of naming to me is catch_unwind as it invokes both the idea that something is being caught but only unwinds as it won't catch panics-as-aborts. The verb "catch" seems interchangeable to me, so something like guard_unwind also seems fine.

For the propagation function, I'd expect something similar like continue_unwind or resume_unwind which clearly indicates that it's only intended for unwinding and isn't useful in a panics-as-aborts world.

@sfackler
Copy link
Member

sfackler commented Apr 5, 2016

I like catch_unwind and resume_unwind unwind personally.

It is worth noting, though, that resume_unwind can still be called in an panic-as-aborts context, it'll just abort without running the panic hook. I don't really think that's too big of a deal though.

@huonw
Copy link
Member

huonw commented Apr 6, 2016

When considering names, the module path is relevant because functions are typically called via it. Things like panic::catch_unwind seem awkward to me: it's three verbs in a row.

Returning #27719 (comment), particularly its suggestion of introducing a submodule to std::panic, unwind::catch_unwind is bad, but maybe unwind::catch is OK? (Modulo the negative connotations of the catch word, of course, and the possibility that catch will become a keyword.)

@aturon
Copy link
Member Author

aturon commented Apr 6, 2016

Like @sfackler, I prefer catch_unwind and resume_unwind, scoped directly in the panic module, rather than introducing a new submodule.

@BurntSushi
Copy link
Member

I think I'm happy as long as unwind is in the name.

@pnkfelix
Copy link
Member

pnkfelix commented Apr 6, 2016

I thought the barrier suggestion from @cybergeek94 was good. Especially if we put it into an unwind submodule, yielding unwind::barrier

To me barrier carries the notion that the unwinding will stop here, but there's the hint that you should perhaps trying to "restart it", to keep it going across the boundary introduced by the barrier, if possible, or at least signal that it occurred.


Update: unwind::guard seems okay too, but for some reason I prefer unwind::barrier; not sure why.

@e-oz
Copy link

e-oz commented Apr 6, 2016

My vote against "barrier", sounds too non-related to execution flow.

On Wed, 6 Apr 2016 at 22:32, Felix S Klock II [email protected]
wrote:

I thought the barrier suggestion from @cybergeek94
https://github.com/cybergeek94 was good. Especially if we put it into
an unwind submodule, yielding unwind::barrier

To me barrier carries the notion that the unwinding will stop here, but
there's the hint that you should perhaps trying to "restart it", to keep it
going across the boundary introduced by the barrier, if possible, or at
least signal that it occurred.


You are receiving this because you were mentioned.

Reply to this email directly or view it on GitHub
#27719 (comment)

@tomaka
Copy link
Contributor

tomaka commented Apr 6, 2016

In system programming, barrier usually means "memory barrier", which has nothing to do with unwinding.

@alexcrichton
Copy link
Member

The libs team discussed this during triage yesterday and the decision was to stabilize with the following names:

  • recover -> catch_unwind
  • propagate -> resume_unwind
  • AssertRecoverSafe -> AssertUnwindSafe

Thanks again for all the discussion everyone!

@strega-nil
Copy link
Contributor

yay :)

alexcrichton added a commit to alexcrichton/rust that referenced this issue Apr 11, 2016
This commit applies all stabilizations, renamings, and deprecations that the
library team has decided on for the upcoming 1.9 release. All tracking issues
have gone through a cycle-long "final comment period" and the specific APIs
stabilized/deprecated are:

Stable

* `std::panic`
* `std::panic::catch_unwind` (renamed from `recover`)
* `std::panic::resume_unwind` (renamed from `propagate`)
* `std::panic::AssertUnwindSafe` (renamed from `AssertRecoverSafe`)
* `std::panic::UnwindSafe` (renamed from `RecoverSafe`)
* `str::is_char_boundary`
* `<*const T>::as_ref`
* `<*mut T>::as_ref`
* `<*mut T>::as_mut`
* `AsciiExt::make_ascii_uppercase`
* `AsciiExt::make_ascii_lowercase`
* `char::decode_utf16`
* `char::DecodeUtf16`
* `char::DecodeUtf16Error`
* `char::DecodeUtf16Error::unpaired_surrogate`
* `BTreeSet::take`
* `BTreeSet::replace`
* `BTreeSet::get`
* `HashSet::take`
* `HashSet::replace`
* `HashSet::get`
* `OsString::with_capacity`
* `OsString::clear`
* `OsString::capacity`
* `OsString::reserve`
* `OsString::reserve_exact`
* `OsStr::is_empty`
* `OsStr::len`
* `std::os::unix::thread`
* `RawPthread`
* `JoinHandleExt`
* `JoinHandleExt::as_pthread_t`
* `JoinHandleExt::into_pthread_t`
* `HashSet::hasher`
* `HashMap::hasher`
* `CommandExt::exec`
* `File::try_clone`
* `SocketAddr::set_ip`
* `SocketAddr::set_port`
* `SocketAddrV4::set_ip`
* `SocketAddrV4::set_port`
* `SocketAddrV6::set_ip`
* `SocketAddrV6::set_port`
* `SocketAddrV6::set_flowinfo`
* `SocketAddrV6::set_scope_id`
* `<[T]>::copy_from_slice`
* `ptr::read_volatile`
* `ptr::write_volatile`
* The `#[deprecated]` attribute
* `OpenOptions::create_new`

Deprecated

* `std::raw::Slice` - use raw parts of `slice` module instead
* `std::raw::Repr` - use raw parts of `slice` module instead
* `str::char_range_at` - use slicing plus `chars()` plus `len_utf8`
* `str::char_range_at_reverse` - use slicing plus `chars().rev()` plus `len_utf8`
* `str::char_at` - use slicing plus `chars()`
* `str::char_at_reverse` - use slicing plus `chars().rev()`
* `str::slice_shift_char` - use `chars()` plus `Chars::as_str`
* `CommandExt::session_leader` - use `before_exec` instead.

Closes rust-lang#27719
cc rust-lang#27751 (deprecating the `Slice` bits)
Closes rust-lang#27754
Closes rust-lang#27780
Closes rust-lang#27809
Closes rust-lang#27811
Closes rust-lang#27830
Closes rust-lang#28050
Closes rust-lang#29453
Closes rust-lang#29791
Closes rust-lang#29935
Closes rust-lang#30014
Closes rust-lang#30752
Closes rust-lang#31262
cc rust-lang#31398 (still need to deal with `before_exec`)
Closes rust-lang#31405
Closes rust-lang#31572
Closes rust-lang#31755
Closes rust-lang#31756
bors added a commit that referenced this issue Apr 12, 2016
std: Stabilize APIs for the 1.9 release

This commit applies all stabilizations, renamings, and deprecations that the
library team has decided on for the upcoming 1.9 release. All tracking issues
have gone through a cycle-long "final comment period" and the specific APIs
stabilized/deprecated are:

Stable

* `std::panic`
* `std::panic::catch_unwind` (renamed from `recover`)
* `std::panic::resume_unwind` (renamed from `propagate`)
* `std::panic::AssertUnwindSafe` (renamed from `AssertRecoverSafe`)
* `std::panic::UnwindSafe` (renamed from `RecoverSafe`)
* `str::is_char_boundary`
* `<*const T>::as_ref`
* `<*mut T>::as_ref`
* `<*mut T>::as_mut`
* `AsciiExt::make_ascii_uppercase`
* `AsciiExt::make_ascii_lowercase`
* `char::decode_utf16`
* `char::DecodeUtf16`
* `char::DecodeUtf16Error`
* `char::DecodeUtf16Error::unpaired_surrogate`
* `BTreeSet::take`
* `BTreeSet::replace`
* `BTreeSet::get`
* `HashSet::take`
* `HashSet::replace`
* `HashSet::get`
* `OsString::with_capacity`
* `OsString::clear`
* `OsString::capacity`
* `OsString::reserve`
* `OsString::reserve_exact`
* `OsStr::is_empty`
* `OsStr::len`
* `std::os::unix::thread`
* `RawPthread`
* `JoinHandleExt`
* `JoinHandleExt::as_pthread_t`
* `JoinHandleExt::into_pthread_t`
* `HashSet::hasher`
* `HashMap::hasher`
* `CommandExt::exec`
* `File::try_clone`
* `SocketAddr::set_ip`
* `SocketAddr::set_port`
* `SocketAddrV4::set_ip`
* `SocketAddrV4::set_port`
* `SocketAddrV6::set_ip`
* `SocketAddrV6::set_port`
* `SocketAddrV6::set_flowinfo`
* `SocketAddrV6::set_scope_id`
* `<[T]>::copy_from_slice`
* `ptr::read_volatile`
* `ptr::write_volatile`
* The `#[deprecated]` attribute
* `OpenOptions::create_new`

Deprecated

* `std::raw::Slice` - use raw parts of `slice` module instead
* `std::raw::Repr` - use raw parts of `slice` module instead
* `str::char_range_at` - use slicing plus `chars()` plus `len_utf8`
* `str::char_range_at_reverse` - use slicing plus `chars().rev()` plus `len_utf8`
* `str::char_at` - use slicing plus `chars()`
* `str::char_at_reverse` - use slicing plus `chars().rev()`
* `str::slice_shift_char` - use `chars()` plus `Chars::as_str`
* `CommandExt::session_leader` - use `before_exec` instead.

Closes #27719
cc #27751 (deprecating the `Slice` bits)
Closes #27754
Closes #27780
Closes #27809
Closes #27811
Closes #27830
Closes #28050
Closes #29453
Closes #29791
Closes #29935
Closes #30014
Closes #30752
Closes #31262
cc #31398 (still need to deal with `before_exec`)
Closes #31405
Closes #31572
Closes #31755
Closes #31756
alexcrichton added a commit to alexcrichton/rust that referenced this issue Apr 12, 2016
This commit applies all stabilizations, renamings, and deprecations that the
library team has decided on for the upcoming 1.9 release. All tracking issues
have gone through a cycle-long "final comment period" and the specific APIs
stabilized/deprecated are:

Stable

* `std::panic`
* `std::panic::catch_unwind` (renamed from `recover`)
* `std::panic::resume_unwind` (renamed from `propagate`)
* `std::panic::AssertUnwindSafe` (renamed from `AssertRecoverSafe`)
* `std::panic::UnwindSafe` (renamed from `RecoverSafe`)
* `str::is_char_boundary`
* `<*const T>::as_ref`
* `<*mut T>::as_ref`
* `<*mut T>::as_mut`
* `AsciiExt::make_ascii_uppercase`
* `AsciiExt::make_ascii_lowercase`
* `char::decode_utf16`
* `char::DecodeUtf16`
* `char::DecodeUtf16Error`
* `char::DecodeUtf16Error::unpaired_surrogate`
* `BTreeSet::take`
* `BTreeSet::replace`
* `BTreeSet::get`
* `HashSet::take`
* `HashSet::replace`
* `HashSet::get`
* `OsString::with_capacity`
* `OsString::clear`
* `OsString::capacity`
* `OsString::reserve`
* `OsString::reserve_exact`
* `OsStr::is_empty`
* `OsStr::len`
* `std::os::unix::thread`
* `RawPthread`
* `JoinHandleExt`
* `JoinHandleExt::as_pthread_t`
* `JoinHandleExt::into_pthread_t`
* `HashSet::hasher`
* `HashMap::hasher`
* `CommandExt::exec`
* `File::try_clone`
* `SocketAddr::set_ip`
* `SocketAddr::set_port`
* `SocketAddrV4::set_ip`
* `SocketAddrV4::set_port`
* `SocketAddrV6::set_ip`
* `SocketAddrV6::set_port`
* `SocketAddrV6::set_flowinfo`
* `SocketAddrV6::set_scope_id`
* `<[T]>::copy_from_slice`
* `ptr::read_volatile`
* `ptr::write_volatile`
* The `#[deprecated]` attribute
* `OpenOptions::create_new`

Deprecated

* `std::raw::Slice` - use raw parts of `slice` module instead
* `std::raw::Repr` - use raw parts of `slice` module instead
* `str::char_range_at` - use slicing plus `chars()` plus `len_utf8`
* `str::char_range_at_reverse` - use slicing plus `chars().rev()` plus `len_utf8`
* `str::char_at` - use slicing plus `chars()`
* `str::char_at_reverse` - use slicing plus `chars().rev()`
* `str::slice_shift_char` - use `chars()` plus `Chars::as_str`
* `CommandExt::session_leader` - use `before_exec` instead.

Closes rust-lang#27719
cc rust-lang#27751 (deprecating the `Slice` bits)
Closes rust-lang#27754
Closes rust-lang#27780
Closes rust-lang#27809
Closes rust-lang#27811
Closes rust-lang#27830
Closes rust-lang#28050
Closes rust-lang#29453
Closes rust-lang#29791
Closes rust-lang#29935
Closes rust-lang#30014
Closes rust-lang#30752
Closes rust-lang#31262
cc rust-lang#31398 (still need to deal with `before_exec`)
Closes rust-lang#31405
Closes rust-lang#31572
Closes rust-lang#31755
Closes rust-lang#31756
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
B-unstable Blocker: Implemented in the nightly compiler and unstable. final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests