-
Notifications
You must be signed in to change notification settings - Fork 451
Add ScopeGuard
and use it in Binder.
#317
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -91,3 +91,66 @@ impl<T: PointerWrapper + Deref> PointerWrapper for Pin<T> { | |
Pin::new_unchecked(T::from_pointer(p)) | ||
} | ||
} | ||
|
||
/// Runs a cleanup function/closure when dropped. | ||
/// | ||
/// The [`ScopeGuard::dismiss`] function prevents the cleanup function from running. | ||
/// | ||
/// # Examples | ||
/// | ||
/// In the example below, we have multiple exit paths and we want to log regardless of which one is | ||
/// taken: | ||
/// ``` | ||
/// fn example1(arg: bool) { | ||
/// let _log = ScopeGuard::new(|| pr_info!("example1 completed\n")); | ||
/// | ||
/// if arg { | ||
/// return; | ||
/// } | ||
/// | ||
/// // Do something... | ||
/// } | ||
/// ``` | ||
/// | ||
/// In the example below, we want to log the same message on all early exits but a different one on | ||
/// the main exit path: | ||
/// ``` | ||
/// fn example2(arg: bool) { | ||
/// let log = ScopeGuard::new(|| pr_info!("example2 returned early\n")); | ||
/// | ||
/// if arg { | ||
/// return; | ||
/// } | ||
/// | ||
/// // (Other early returns...) | ||
/// | ||
/// log.dismiss(); | ||
/// pr_info!("example2 no early return\n"); | ||
/// } | ||
/// ``` | ||
pub struct ScopeGuard<T: FnOnce()> { | ||
cleanup_func: Option<T>, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This could be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, it could. But the size of the object is 1 byte, which I expect to be optimised out on optimised builds anyway. I don't think adding There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's indeed usually optimised out, but having a known-to-be-true if statement in the drop method still feels awkward. (BTW it's There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Haha, I know you're going to say this :) I only realized that after I send out that message. Obviously you can add a
Also, please add There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also LTO probably means that we would need LLD somewhere in our pipeline? Anyway I still think we shouldn't require or assume people will use LTO. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. By the way, please do not take me the wrong way when I complain about stuff like this. I usually push you guys a lot (@nbdd0121 and @bjorn3) because I know you are experts on this stuff. The idea being that either 1) you tell me how I am completely stupid (and then I learn more about There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I was thinking LLVM could give back its cost heuristics after optimizing a function, and then the frontend decide if it wants to generate a Rust generic-like function or go with a normal one etc. Yeah, it is likely not trivial if LLVM is not setup to do that, and most likely not worth it anyway since using LTO is simpler and a more general approach. But hey, it would improve things for all non-LTO-enabled projects and remove the needs for so many hints.
Yes, but what I am saying is the amount of hints we use depends on whether we say LTO is the way to go or not, and how much the overall % lost. The idea being that we could try to be better than C here and have the hints that actually matter, instead of systematically providing hints everywhere. At the current time, the safest approach is indeed doing it manually like the C side does (providing hints, having functions in headers, using macros to hack it, etc.). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yeah:
It is still experimental, but since it is also a requirement for CFI, I bet it will become way more common soon enough. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Rustc's LTO support normally doesn't require LLD as rustc performs it itself. Due to the way rustc is used, it can't do LTO itself though and will need linker plugin LTO. This should work with linkers other than LLD though, but requires a linker plugin for the right LLVM version rustc uses.
By the time codegen happens, the frontend has already emitted the metadata file. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not even a nit, probably closer to a code style question. Why name the field if it's the only one in the struct? Presumably you could also do: pub struct ScopeGuard<T: FnOnce()>(Option<T>); Is this because an explicit name provides more information than There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, we should decide which style to use. One tiny advantage of non-tuple ones is avoiding tuple constructor shenanigans:
I would like to have a lint for that -- from a quick look there does not seem to be one. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sometimes this is an advantage, e.g. you can use the tuple struct name in functions such as Obviously for non-public structs there's no difference though. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think a simple rule would be to avoid tuple ones unless the tuple constructor is intended to be used, e.g. for newtypes. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It is just the bottom bits of the address: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=a36bc1e4b100255e9f2c4e5b7786d5d1 Notice There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So you're saying that Rust leaks memory addresses just like that...? I'm not sure what to think about that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From my point of view, this discussion shouldn't derail @wedsonaf 's PR. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Well, it is memory-safe to do so on its own ;) (More seriously: you are right, in the kernel we definitely want to avoid leaking addresses -- but that is a bit orthogonal to this).
No worries! (I will be logging off for the weekend myself in a few minutes anyway -- I guess Wedson will add the updated docs on Monday). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Have a great week-end. Thanks for updating my understanding of Rust! |
||
} | ||
|
||
impl<T: FnOnce()> ScopeGuard<T> { | ||
/// Creates a new cleanup object with the given cleanup function. | ||
pub fn new(cleanup_func: T) -> Self { | ||
Self { | ||
cleanup_func: Some(cleanup_func), | ||
} | ||
} | ||
|
||
/// Prevents the cleanup function from running. | ||
pub fn dismiss(mut self) { | ||
self.cleanup_func.take(); | ||
} | ||
} | ||
|
||
impl<T: FnOnce()> Drop for ScopeGuard<T> { | ||
fn drop(&mut self) { | ||
// Run the cleanup function if one is still present. | ||
if let Some(cleanup) = self.cleanup_func.take() { | ||
ojeda marked this conversation as resolved.
Show resolved
Hide resolved
|
||
cleanup(); | ||
} | ||
} | ||
} |
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.
Please add two or three examples, e.g. a trivial one with a explicit scope for extra clarity, one with
dismiss()
to show the difference and one with e.g. the?
operator etc. in the middle (like in your use case in Binder).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.
Added two examples.