-
Notifications
You must be signed in to change notification settings - Fork 13k
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
Look into drop powered panic guards #103101
Comments
Strong -1 from me. That's a lot of churn and there are still totally reasonable cases to use More concretely, I think that |
The point that came up in #102023 was that |
It's also reasonable to use if you want to run a function that returns an RAII type and just prevent its destructor from running. That is, cases like I agree that perhaps it's in the wrong module, but I don't think it's worth the degree of ecosystem churn that would be required to change it (I also don't know where else would be better). |
That is exactly the guard case I mentioned, which maybe should be I am not suggesting to deprecate |
I think it would be pretty confusing for a MutexGuard, and I don't really think a dedicated method makes sense in all cases (it's quite rare that you want to leave a lock locked). |
See also #62553. It's true that I'd also note that a large part of the docs for |
I think a better solution there is to not make |
That's rust-lang/unsafe-code-guidelines#326. Are you suggesting we entirely remove the |
For the Regarding the
The Proposal: rewrite all panic guards to always drop and then check for |
Checking It seems better to abstract this pattern into a helper so not each and every user needs to get the |
I don't know how thread::panicking works: why would it be fragile? Also saying thread::panicking is more fragile seems wrong. The current approach is: let me think really hard about all the places I return stuff to the user and where I could panic and then make sure I forget in all the right places but not before I could panic. The new approach would be: just check for panicking inside Drop and deallocate if so. To me, that's way easier and clearer.
I would prefer that to checking panicking, but I don't think it's possible to express "drop when panicking but not under normal operation"? Or are you thinking of a |
I am thinking of a function that takes 2 closures, the 2nd one to be executed only if a panic occurs while the first is running. |
I feel much more strongly about Vec/String/etc than Box, but yeah, I'd prefer those be NonNull than Unique (and everything that implies). For Box, I'm not sure. If we don't have evidence that it helps performance I think we should go with NonNull, though.
Performance. Checking |
Ah, dang. Found the implementation: rust/library/std/src/panicking.rs Lines 373 to 388 in 9a97cc8
Why? I think I'm missing something here because this is the analogy I'm seeing: char* ptr = malloc(...); // Equivalent: PanicGuard::new()
// ...
free(ptr); // Equivalent: forget(guard). If you don't put this in, you're sad in C but much sadder with the guard because it's a UAF.
Interesting, so this-ish: fn panic_guard<T>(block: impl FnOnce() -> T, panic_handler: impl FnOnce()) -> T {
struct PanicGuard<PanicHandler: FnOnce()>(PanicHandler);
impl<P: FnOnce()> Drop for PanicGuard<P> {
fn drop(&mut self) {
self.0();
}
}
let guard = PanicGuard(panic_handler);
let result = block();
std::mem::forget(guard);
result
} I'd have to see what that looks like at the call site, but I think that makes sense. |
The aliasing requirements we could make (as defined by Stacked Borrows) go well beyond what can be expressed in LLVM, so currently we don't have a way of evaluating their performance impact. |
Yeah, except both closures probably want to share some state so we might have to introduce another parameter that is mutably borrowed by both. |
I would like to mention the problems the existence of forget and ManualDrop causes in parallelism: by allowing freeing memory without calling destructors, it becomes impossible to notify the other threads that this memory is no longer safe to use, blocking until they get it, and maybe migrate the data somewhere else. This forbids implementing the async variant of scope, sharing the parent future data with child futures. It seems like it is more right to implement defusing resources, such as guards and file descriptors, on an individual basis, not with a ubiquitous forget. |
@faucct it was basically decided that deprecating forget isn't going to happen. I'm only keeping this issue open to see if there's a better way to handle panic guards. |
@faucct ManuallyDrop/mem::forget are a red herring, you can implement leaking memory in safe code with just |
Yeah, I have already found the weird examples, where though the memory is not being reclaimed without the destructors being run, but the references stored in it are becoming unborrowed. |
Origin: #102023 (comment)
It's a bit weird to have
forget
andManuallyDrop
, especially since forget is just alet _ = ManuallyDrop::new(t);
under the hood. The primary usage offorget
that still makes sense is for panic guards, but usingforget
has the disadvantage of being unclear in its intent. Removingforget
would probably make panic guards clearer as they would encourage writing a comment or seperate method explaining what's happening.The text was updated successfully, but these errors were encountered: