Skip to content

Commit

Permalink
Merge pull request torvalds#317 from wedsonaf/cleanup
Browse files Browse the repository at this point in the history
Add `ScopeGuard` and use it in Binder.
  • Loading branch information
ojeda authored Jun 1, 2021
2 parents 7884043 + c96a4c5 commit ac536cc
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 9 deletions.
20 changes: 12 additions & 8 deletions drivers/android/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use alloc::sync::Arc;
use core::sync::atomic::{AtomicBool, Ordering};
use kernel::{
io_buffer::IoBufferWriter, linked_list::Links, prelude::*, sync::Ref,
user_ptr::UserSlicePtrWriter,
user_ptr::UserSlicePtrWriter, ScopeGuard,
};

use crate::{
Expand Down Expand Up @@ -144,6 +144,10 @@ impl DeliverToRead for Transaction {
pub sender_pid: pid_t,
pub sender_euid: uid_t,
*/
let send_failed_reply = ScopeGuard::new(|| {
let reply = Either::Right(BR_FAILED_REPLY);
self.from.deliver_reply(reply, &self);
});
let mut tr = BinderTransactionData::default();

if let Some(nref) = &self.node_ref {
Expand Down Expand Up @@ -171,13 +175,13 @@ impl DeliverToRead for Transaction {
BR_TRANSACTION
};

// Write the transaction code and data to the user buffer. On failure we complete the
// transaction with an error.
if let Err(err) = writer.write(&code).and_then(|_| writer.write(&tr)) {
let reply = Either::Right(BR_FAILED_REPLY);
self.from.deliver_reply(reply, &self);
return Err(err);
}
// Write the transaction code and data to the user buffer.
writer.write(&code)?;
writer.write(&tr)?;

// Dismiss the completion of transaction with a failure. No failure paths are allowed from
// here on out.
send_failed_reply.dismiss();

// When this is not a reply and not an async transaction, update `current_transaction`. If
// it's a reply, `current_transaction` has already been updated appropriately.
Expand Down
2 changes: 1 addition & 1 deletion rust/kernel/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ pub mod user_ptr;
pub use build_error::build_error;

pub use crate::error::{Error, Result};
pub use crate::types::Mode;
pub use crate::types::{Mode, ScopeGuard};

/// Page size defined in terms of the `PAGE_SHIFT` macro from C.
///
Expand Down
63 changes: 63 additions & 0 deletions rust/kernel/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
}

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() {
cleanup();
}
}
}

0 comments on commit ac536cc

Please sign in to comment.