Skip to content

Commit 4946eec

Browse files
authored
Merge pull request torvalds#541 from wedsonaf/scope-guard
rust: improve `ScopeGuard`.
2 parents 8df76b5 + 876233b commit 4946eec

File tree

1 file changed

+68
-15
lines changed

1 file changed

+68
-15
lines changed

rust/kernel/types.rs

Lines changed: 68 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,13 @@ use crate::{
99
sync::{Ref, RefBorrow},
1010
};
1111
use alloc::boxed::Box;
12-
use core::{cell::UnsafeCell, mem::MaybeUninit, ops, ops::Deref, pin::Pin, ptr::NonNull};
12+
use core::{
13+
cell::UnsafeCell,
14+
mem::MaybeUninit,
15+
ops::{self, Deref, DerefMut},
16+
pin::Pin,
17+
ptr::NonNull,
18+
};
1319

1420
/// Permissions.
1521
///
@@ -200,29 +206,76 @@ impl<T: PointerWrapper + Deref> PointerWrapper for Pin<T> {
200206
/// pr_info!("example2 no early return\n");
201207
/// }
202208
/// ```
203-
pub struct ScopeGuard<T: FnOnce()> {
204-
cleanup_func: Option<T>,
209+
///
210+
/// In the example below, we need a mutable object (the vector) to be accessible within the log
211+
/// function, so we wrap it in the [`ScopeGuard`]:
212+
/// ```
213+
/// # use kernel::prelude::*;
214+
/// # use kernel::ScopeGuard;
215+
/// fn example3(arg: bool) -> Result {
216+
/// let mut vec =
217+
/// ScopeGuard::new_with_data(Vec::new(), |v| pr_info!("vec had {} elements\n", v.len()));
218+
///
219+
/// vec.try_push(10u8)?;
220+
/// if arg {
221+
/// return Ok(());
222+
/// }
223+
/// vec.try_push(20u8)?;
224+
/// Ok(())
225+
/// }
226+
/// ```
227+
///
228+
/// # Invariants
229+
///
230+
/// The value stored in the struct is nearly always `Some(_)`, except between
231+
/// [`ScopeGuard::dismiss`] and [`ScopeGuard::drop`]: in this case, it will be `None` as the value
232+
/// will have been returned to the caller. Since [`ScopeGuard::dismiss`] consumes the guard,
233+
/// callers won't be able to use it anymore.
234+
pub struct ScopeGuard<T, F: FnOnce(T)>(Option<(T, F)>);
235+
236+
impl<T, F: FnOnce(T)> ScopeGuard<T, F> {
237+
/// Creates a new guarded object wrapping the given data and with the given cleanup function.
238+
pub fn new_with_data(data: T, cleanup_func: F) -> Self {
239+
// INVARIANT: The struct is being initialised with `Some(_)`.
240+
Self(Some((data, cleanup_func)))
241+
}
242+
243+
/// Prevents the cleanup function from running and returns the guarded data.
244+
pub fn dismiss(mut self) -> T {
245+
// INVARIANT: This is the exception case in the invariant; it is not visible to callers
246+
// because this function consumes `self`.
247+
self.0.take().unwrap().0
248+
}
205249
}
206250

207-
impl<T: FnOnce()> ScopeGuard<T> {
208-
/// Creates a new cleanup object with the given cleanup function.
209-
pub fn new(cleanup_func: T) -> Self {
210-
Self {
211-
cleanup_func: Some(cleanup_func),
212-
}
251+
impl ScopeGuard<(), Box<dyn FnOnce(())>> {
252+
/// Creates a new guarded object with the given cleanup function.
253+
pub fn new(cleanup: impl FnOnce()) -> ScopeGuard<(), impl FnOnce(())> {
254+
ScopeGuard::new_with_data((), move |_| cleanup())
213255
}
256+
}
257+
258+
impl<T, F: FnOnce(T)> Deref for ScopeGuard<T, F> {
259+
type Target = T;
260+
261+
fn deref(&self) -> &T {
262+
// The type invariants guarantee that `unwrap` will succeed.
263+
&self.0.as_ref().unwrap().0
264+
}
265+
}
214266

215-
/// Prevents the cleanup function from running.
216-
pub fn dismiss(mut self) {
217-
self.cleanup_func.take();
267+
impl<T, F: FnOnce(T)> DerefMut for ScopeGuard<T, F> {
268+
fn deref_mut(&mut self) -> &mut T {
269+
// The type invariants guarantee that `unwrap` will succeed.
270+
&mut self.0.as_mut().unwrap().0
218271
}
219272
}
220273

221-
impl<T: FnOnce()> Drop for ScopeGuard<T> {
274+
impl<T, F: FnOnce(T)> Drop for ScopeGuard<T, F> {
222275
fn drop(&mut self) {
223276
// Run the cleanup function if one is still present.
224-
if let Some(cleanup) = self.cleanup_func.take() {
225-
cleanup();
277+
if let Some((data, cleanup)) = self.0.take() {
278+
cleanup(data)
226279
}
227280
}
228281
}

0 commit comments

Comments
 (0)