diff options
author | Alice Ryhl <aliceryhl@google.com> | 2023-08-28 10:48:06 +0000 |
---|---|---|
committer | Tejun Heo <tj@kernel.org> | 2023-09-25 09:46:42 -1000 |
commit | 115c95e9e14c482682080598907e6a18091fd0af (patch) | |
tree | 1628ae3751ab583e7b802f6b415430c37ddead7c /rust | |
parent | 47f0dbe8fdc28e6e79505b640619e0cbccb91cef (diff) | |
download | lwn-115c95e9e14c482682080598907e6a18091fd0af.tar.gz lwn-115c95e9e14c482682080598907e6a18091fd0af.zip |
rust: workqueue: add `try_spawn` helper method
This adds a convenience method that lets you spawn a closure for
execution on a workqueue. This will be the most convenient way to use
workqueues, but it is fallible because it needs to allocate memory.
Co-developed-by: Gary Guo <gary@garyguo.net>
Signed-off-by: Gary Guo <gary@garyguo.net>
Signed-off-by: Alice Ryhl <aliceryhl@google.com>
Reviewed-by: Martin Rodriguez Reboredo <yakoyoku@gmail.com>
Reviewed-by: "Andreas Hindborg (Samsung)" <nmi@metaspace.dk>
Reviewed-by: Benno Lossin <benno.lossin@proton.me>
Reviewed-by: Boqun Feng <boqun.feng@gmail.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
Diffstat (limited to 'rust')
-rw-r--r-- | rust/kernel/workqueue.rs | 43 |
1 files changed, 43 insertions, 0 deletions
diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs index c1334c48b96b..40ccc53f6e93 100644 --- a/rust/kernel/workqueue.rs +++ b/rust/kernel/workqueue.rs @@ -29,6 +29,7 @@ //! C header: [`include/linux/workqueue.h`](../../../../include/linux/workqueue.h) use crate::{bindings, prelude::*, sync::Arc, sync::LockClassKey, types::Opaque}; +use alloc::alloc::AllocError; use alloc::boxed::Box; use core::marker::PhantomData; use core::pin::Pin; @@ -96,6 +97,44 @@ impl Queue { }) } } + + /// Tries to spawn the given function or closure as a work item. + /// + /// This method can fail because it allocates memory to store the work item. + pub fn try_spawn<T: 'static + Send + FnOnce()>(&self, func: T) -> Result<(), AllocError> { + let init = pin_init!(ClosureWork { + work <- new_work!("Queue::try_spawn"), + func: Some(func), + }); + + self.enqueue(Box::pin_init(init).map_err(|_| AllocError)?); + Ok(()) + } +} + +/// A helper type used in `try_spawn`. +#[pin_data] +struct ClosureWork<T> { + #[pin] + work: Work<ClosureWork<T>>, + func: Option<T>, +} + +impl<T> ClosureWork<T> { + fn project(self: Pin<&mut Self>) -> &mut Option<T> { + // SAFETY: The `func` field is not structurally pinned. + unsafe { &mut self.get_unchecked_mut().func } + } +} + +impl<T: FnOnce()> WorkItem for ClosureWork<T> { + type Pointer = Pin<Box<Self>>; + + fn run(mut this: Pin<Box<Self>>) { + if let Some(func) = this.as_mut().project().take() { + (func)() + } + } } /// A raw work item. @@ -365,6 +404,10 @@ macro_rules! impl_has_work { )*}; } +impl_has_work! { + impl<T> HasWork<Self> for ClosureWork<T> { self.work } +} + unsafe impl<T, const ID: u64> WorkItemPointer<ID> for Arc<T> where T: WorkItem<ID, Pointer = Self>, |