-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Abort by default v2 #1765
Abort by default v2 #1765
Conversation
If I specify |
@gnzlbg Yes. |
@gbzlbg It does not. |
@rkruppe Are you sure about that? If so, I'll add it to the RFC. |
I tested it and reportedmy findings in the old thread. It's easy to try for yourself, too. |
# Detailed design | ||
[design]: #detailed-design | ||
|
||
Have `cargo` generate `panic=abort` to the `Cargo.toml`. Whenever the user inteacts with `cargo` (by using a command) and the `Cargo.toml` has no specified panicking strategy, it will add `panic=unwind` and leave a note to the user: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: inteacts -> interacts
|
||
Warning: No panic strategy was specified, added unwinding to the `Cargo.toml` file, please consider change it to your needs. | ||
|
||
For libraries, nothing is modified, as they inherit the strategy of the binary. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens for crates with both? I believe this is a fairly common pattern; I assume they would set it to unwind since they have the binary component?
Use unwind in `debug` and abort in `release`. | ||
|
||
Make use of more exotic instructions like `bound`, allowing e.g. overflow or bound checking without branches. This comes at the expense of error output. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I remember seeing stack maps discussed as an alternative, can someone speak to that? I don't know much about whether those are beneficial here/related to this....
It seems to me that we should make the panic strategy always explicit, and if a library can work with both |
I don't quite understand why we should force a program into aborting as a binary. Is it possible that the program was specifically designed to take advantage of unwinding and that removing that possibility could hurt the performance/reliability in some way? Maybe the performance hit from unwinding is negligible for a certain application, or the program is (hopefully not) developed in a way that unwinding is required for it to function correctly. I do not think a programmer should be forced into using one method of recovering from errors just because it might save space or decrease compile time. |
Chances are that it's not, and thus aborting is IMO a saner default. You don't "force" the program into anything, you change the default, and if you believe your program is better off w/ unwinding, then you can just change the default. |
I might be wrong, but won't abort by default make hyper and it's stack unable to survive panics in the request handler (without changing the behavior to unwinding)? I believe the RFC should discuss the impact on libtest (which depends on non-abort), hyper/iron, and other programs which likely need to survive a panic. While changing the panic behavior is an option, I don't believe it should be required to alter defaults when adding tests. I believe @ticki is arguing that changing the default is easy when required, but I think it still is an overhead, especially for newcomers to the language, that we should try to avoid. |
One pillar of Rust is zero-cost abstractions. This goes hand-in-hand with "don't pay for what you don't use". Currently, the default is that all binaries pay for unwinding in terms of performance, binary size, and compilation speed, with respect to aborting, even if they do not use unwinding or anything that does. This RFC just argues that it would be a better default for new projects to abort by default, and if they want to use a library that requires unwinding (or use unwinding themselves), then opt into it. Instead of the other way around. As a side-effect, this discourages library writers even more from writing libraries that implicitly assume that most binaries will use unwinding as a panicking strategy. This might encourage library writers to assume that most binaries use abort as a panicking strategy, but this might be a good thing. Since while panics should only be used for fatal unrecoverable errors, it is tempting for some authors to use them for error handling just because they think they might get away with it. Switching to abort as a default is a clear statement that this isn't the intended usage of panic, which I think is a nice side-effect of this RFC, but not the main reason while it is being proposed. The main reason is "pay for what you use, don't pay for what you don't use, by default". |
Another argument that I see is, as far as I know, there has been discussion treating OOM and other similar conditions as "panic" which could then be caught when necessary. However, despite my earlier comments, I think this RFC in general is a good idea especially considering the "don't pay for what you don't use" argument. However, I believe that one thing to consider is that rustc itself has to use unwinding (as far as I know), and I suspect this will be common. It would be interesting and helpful to have some numbers as to the percent of crates that depend at some level on unwinding instead of aborting. |
@Mark-Simulacrum Your 2nd last comment is a good point. I've tried to address it in the latest commit. |
@ticki I'd also add a benefit on simpler programming model---use what you need in terms of cognitive burden too. One hitch is that as per rust-lang/rust#32837 (comment) I don't think the current Cargo option is very well designed. Relatedly, post-needs-provides I'd like rustc to only think of the ABI concerns of various panic strategies, not the strategies themselves. Together this means while I am 100% on board the goals of this RFC, I also don't want to stabilize Cargo's current interface any time soon :/. |
|
||
Keep unwinding as default. | ||
|
||
Make Cargo set `panic=abort` in all new binaries. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is no longer an alternative but the main proposal.
Regarding the unresolved question, searching for catch_unwind is a start, but I believe at most (probably a subset of) all crates which spawn threads depend on unwinding. For example, Iron and Hyper both do not have catch_unwind in their codebase, but do catch unwinds at the thread boundary. Will the proposed |
As I understand it, if one uses Would it be feasibly possible to allow interconnected libraries/crates to implement differing For example: library A uses The process might be like this:
|
While I'd like to get abort by default, I'm not sure this is really the best way to do it: Crates requiring panic=unwind is (in some sense) an unstated dependency (of a sort: a crate "depending" on panic=unwind would, in a conservative setup, require that all other crates it links to are built with panic=unwind), and I'd much rather we have a way for that dependency to be stated rather than requiring the end consumer crate to know. This RFC still seems to try to work within the existing cargo configuration, which I don't think can represent that dependency. Anyhow, a more specific RFC comment:
|
No, this scenario works. I'm also fuzzy on what the benefit of such a change would be. |
Regarding hyper: in version 0.9.x, it does spawn threads (since thats required to handle more than 1 connection thanks to synchronous IO), it doesn't depend on the the threads catching panics. If the process aborted instead, that'd be fine. That may not be something a user wants of their server, and instead wants the other connections to be able to safely shutdown or whatever, but that's not something hyper cares about. In version 0.10, once using async IO, hyper won't even be spawning threads, so it's even less of an issue. Instead, any panic during the event loop means it could all die in a fire. |
|
||
## Libraries | ||
|
||
For libraries, the `Cargo.toml` is not modified, as they inherit the strategy of the binary. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This conflicts with the above, which says a default of any
will be added.
One aspect that the RFC, and most of the discussion so far, has ignored is that the panic strategy is per profile. There is not one panic strategy but potentially many, and in fact I will later argue that they should not all be the same. I build some crates with
Furthermore, unwinding is virtually unavoidable for testing, since it's needed for Additionally, it has been pointed that there are some downsides to 99% of projects being built with the same panic strategy, regardless of which one it is:
Therefore, it seems good to "mix things up". That is, if I could choose what panic strategy 99% of crates should have, I would opt for:
|
The only way I could see this working, would be to require a crate to explicitly state panic=unwind or panic=any in order to make 'catch_unwind' available. This way any crate that relies on unwind will automatically need annotations. All other crates should not have to specify anything and thus should follow the dependee. Crates can always explicitly set panic={unwind,abort,any} and all when compiling a program all crates should have compatible settings. But the idea is that almost no library crate should need such an explicit setting and mismatches should thus very rarely occur. The behaviour of thread exit is not tied to thus setting, since it represents a totally different isolation boundary. The most common form if panic recovery in this case is around worker pools and letting the panic policy be controlled by the dependee is idiomatic in this case. In the very rare case that a crate is absolutely relying on error recover through threads panicing it can still explictly state panic=unwind. |
|
@sfackler Fair enough; forget about unsafe - I meant to say that if you want to avoid leaking memory, you likely have more important things to worry about (mem::forget, Rc cycles, etc). And if panicking is something that's going to happen regularly during the lifetime of your program, surely the middle ground panic mode is not for you. And all I'm asking for is that you give it a thought and evaluate the upsides of my proposal, before just dismissing it because of what you see as one specific downside? |
In what way are mem::forget, Rc cycles, etc "more important things" than all of the memory associated with a a thread leaking all at once? Is this some kind of absolutist mindset of "unless we can formally verify that literally no memory will leak forever, we may as well leak all of it"? Adding a new unwinding strategy has costs. Library authors like @nikomatsakis will need to go back and review their code to decide if it is or is not sound in the presence of this new strategy. It is yet another subtle variant of unwinding strategies a user considering their pick will need to think about. How common is this middle ground use case of not wanting to pay whatever cost landing pads have, but still want to do some amount of cleanup after a panic (that I guess is recognized at the thread join?) before exiting? |
Well, you can implement |
They are more important because they can happen in non-panicking code.
No; it's because panicking happens so rarely, I'm not willing to pay any performance cost in my ordinary non-panicking code.
Only if you rely on a specific destructor behaviour to be safe, which the nomicon explicitly says that you cannot do. (Side note: Thinking more about it, perhaps rayon can replace its current strategy with a
If you're writing a library, to be called by a non-Rust host process, and also want maximum performance, this middle-ground mode is your best option. Because killing a host process is not nice behaviour; you don't know if the host process wants to clean something up. For my own part, I'd just use it by default everywhere. |
Leaking memory of your host process until it gets OOM killed is also not nice behavior, and is in some ways worse since it can be far more difficult to track down the cause. |
That is unlikely to happen for two reasons:
|
Memory leaking is a no-no, and honestly I think that this discussion has gone way out of the actual scope |
But So I disagree with that statement. |
First of all, it's pretty rare that'll happen. Second of all, if it does it is a ridiculously small amount. By leaking everything owned in the call stack, you leak potentially enormous amounts of memory.
Like what? Files, resources, virtual memory, poll queues, etc. etc. are all cleared for you. |
@ticki a quick googling came up with this stackoverflow link for a list of things not cleaned up by Windows. Other operating systems may have other lists of things they do not clean up. |
I think the RFC should mention that certain parts of std and possible other libraries should not be usable => cause an error on compilation currently this is mainly With this if a library uses a part marked with it and |
@dathinab Using catch_unwind is not necessarily indicative of not being compatible with abort. |
@ticki your right using But, if you specify Also I would guess that many crates where The only situation I can think of where
So yes, erroring on
|
Wrong thread. |
Cross-linking to another discussion of this RFC and related themes. TLDR: |
Debug builds should definitely unwind. Performance is irrelevant here; being able to log a detailed error is. |
@rfcbot fcp close There hasn't been a lot of activity on this thread lately. I think the basic tradeoffs are well understood, so let me summarize:
I think ultimately there isn't enough reason to make a change here. Hence I nominate that we close this RFC. Thoughts? |
Team member @nikomatsakis has proposed to close this. The next step is review by the rest of the tagged teams:
No concerns currently listed. Once these reviewers reach consensus, this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See this document for info about what commands tagged team members can give me. |
@rfcbot reviewed |
I agree that switching to abort by default isn't something to go for right now. |
The bot appears to be stuck, but everyone has signed off, so: 🔔 This RFC is entering its final comment period with disposition to close. 🔔 See the summary for more details. |
😢 |
The final comment period for this RFC has elapsed. There hasn't been any further discussion since the lang team summary, so I'm going to go ahead and close. Thanks, @ticki for the RFC! |
This version is updated to reflect all the concerns mentioned in the previous proposal. Especially the concerns for stability are now fixed. Instead of switching the default, we switch what
cargo new
generates as well as makingcargo build
addpanic=unwind
to smoothen the transition to mandatory choice of panicking strategy.Rendered
cc. @Ericson2314 @eddyb @rkruppe @petrochenkov @apasel422 @steveklabnik @sfackler @Amanieu @frewsxcv @aturon @brson @Valloric @gnzlbg @nikomatsakis (participants in the previous thread).