Commit 4d4692a2 authored by Wedson Almeida Filho's avatar Wedson Almeida Filho Committed by Miguel Ojeda

rust: types: introduce `ScopeGuard`

This allows us to run some code when the guard is dropped (e.g.,
implicitly when it goes out of scope). We can also prevent the
guard from running by calling its `dismiss()` method.
Signed-off-by: default avatarWedson Almeida Filho <wedsonaf@gmail.com>
Reviewed-by: default avatarVincenzo Palazzo <vincenzopalazzodev@gmail.com>
Reviewed-by: default avatarAndreas Hindborg <a.hindborg@samsung.com>
Signed-off-by: default avatarMiguel Ojeda <ojeda@kernel.org>
parent dec1df54
......@@ -2,7 +2,131 @@
//! Kernel types.
use core::{cell::UnsafeCell, mem::MaybeUninit};
use core::{
cell::UnsafeCell,
mem::MaybeUninit,
ops::{Deref, DerefMut},
};
/// 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:
/// ```
/// # use kernel::ScopeGuard;
/// fn example1(arg: bool) {
/// let _log = ScopeGuard::new(|| pr_info!("example1 completed\n"));
///
/// if arg {
/// return;
/// }
///
/// pr_info!("Do something...\n");
/// }
///
/// # example1(false);
/// # example1(true);
/// ```
///
/// In the example below, we want to log the same message on all early exits but a different one on
/// the main exit path:
/// ```
/// # use kernel::ScopeGuard;
/// 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");
/// }
///
/// # example2(false);
/// # example2(true);
/// ```
///
/// In the example below, we need a mutable object (the vector) to be accessible within the log
/// function, so we wrap it in the [`ScopeGuard`]:
/// ```
/// # use kernel::ScopeGuard;
/// fn example3(arg: bool) -> Result {
/// let mut vec =
/// ScopeGuard::new_with_data(Vec::new(), |v| pr_info!("vec had {} elements\n", v.len()));
///
/// vec.try_push(10u8)?;
/// if arg {
/// return Ok(());
/// }
/// vec.try_push(20u8)?;
/// Ok(())
/// }
///
/// # assert_eq!(example3(false), Ok(()));
/// # assert_eq!(example3(true), Ok(()));
/// ```
///
/// # Invariants
///
/// The value stored in the struct is nearly always `Some(_)`, except between
/// [`ScopeGuard::dismiss`] and [`ScopeGuard::drop`]: in this case, it will be `None` as the value
/// will have been returned to the caller. Since [`ScopeGuard::dismiss`] consumes the guard,
/// callers won't be able to use it anymore.
pub struct ScopeGuard<T, F: FnOnce(T)>(Option<(T, F)>);
impl<T, F: FnOnce(T)> ScopeGuard<T, F> {
/// Creates a new guarded object wrapping the given data and with the given cleanup function.
pub fn new_with_data(data: T, cleanup_func: F) -> Self {
// INVARIANT: The struct is being initialised with `Some(_)`.
Self(Some((data, cleanup_func)))
}
/// Prevents the cleanup function from running and returns the guarded data.
pub fn dismiss(mut self) -> T {
// INVARIANT: This is the exception case in the invariant; it is not visible to callers
// because this function consumes `self`.
self.0.take().unwrap().0
}
}
impl ScopeGuard<(), fn(())> {
/// Creates a new guarded object with the given cleanup function.
pub fn new(cleanup: impl FnOnce()) -> ScopeGuard<(), impl FnOnce(())> {
ScopeGuard::new_with_data((), move |_| cleanup())
}
}
impl<T, F: FnOnce(T)> Deref for ScopeGuard<T, F> {
type Target = T;
fn deref(&self) -> &T {
// The type invariants guarantee that `unwrap` will succeed.
&self.0.as_ref().unwrap().0
}
}
impl<T, F: FnOnce(T)> DerefMut for ScopeGuard<T, F> {
fn deref_mut(&mut self) -> &mut T {
// The type invariants guarantee that `unwrap` will succeed.
&mut self.0.as_mut().unwrap().0
}
}
impl<T, F: FnOnce(T)> Drop for ScopeGuard<T, F> {
fn drop(&mut self) {
// Run the cleanup function if one is still present.
if let Some((data, cleanup)) = self.0.take() {
cleanup(data)
}
}
}
/// Stores an opaque value.
///
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment