[RFC PATCH] rust: workqueue: Add an example for try_spawn()

Boqun Feng posted 1 patch 2 months ago
rust/kernel/workqueue.rs | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
[RFC PATCH] rust: workqueue: Add an example for try_spawn()
Posted by Boqun Feng 2 months ago
`try_spawn()` could use an example to demonstrate the usage, and
arguably it's the most simple usage of workqueue in case someone needs a
deferred work, so add it.

Cc: Joel Fernandes <joelagnelf@nvidia.com>
Cc: "Paul E. McKenney" <paulmck@kernel.org>
Signed-off-by: Boqun Feng <boqun.feng@gmail.com>
---
Miguel, Alice and Tejun, while I'm at it, should we also rename the
function to `spawn()` because of the motivation mentioned here [1]?

[1]: https://rust-for-linux.zulipchat.com/#narrow/channel/291566-Library/topic/.60new.60.20or.20.60try_new.60.3F/near/529533317

Also I find the `{ <clone> || { } }` is really good if I only need to
clone the Arc for passing to a callback closure, but I'm not sure how
people feel about it, so criticism is welcome ;-)

 rust/kernel/workqueue.rs | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs
index b9343d5bc00f..59c1a5e14d12 100644
--- a/rust/kernel/workqueue.rs
+++ b/rust/kernel/workqueue.rs
@@ -331,6 +331,33 @@ pub fn enqueue_delayed<W, const ID: u64>(&self, w: W, delay: Jiffies) -> W::Enqu
     /// 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.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use kernel::{alloc::flags, sync::{Arc, Completion, new_spinlock}, workqueue};
+    ///
+    /// let work_done = Arc::pin_init(Completion::new(), flags::GFP_KERNEL)?;
+    /// let data = Arc::pin_init(new_spinlock!(0), flags::GFP_KERNEL)?;
+    ///
+    /// workqueue::system().try_spawn(
+    ///     flags::GFP_KERNEL,
+    ///     {
+    ///         let work_done = work_done.clone();
+    ///         let data = data.clone();
+    ///         move || {
+    ///             *data.lock() = 42;
+    ///             work_done.complete_all();
+    ///         }
+    ///     }
+    /// )?;
+    ///
+    /// work_done.wait_for_completion();
+    ///
+    /// // `work_done` being completed implies the observation of the write of `data` in the work.
+    /// assert_eq!(*data.lock(), 42);
+    /// # Ok::<(), Error>(())
+    /// ```
     pub fn try_spawn<T: 'static + Send + FnOnce()>(
         &self,
         flags: Flags,
-- 
2.39.5 (Apple Git-154)
Re: [RFC PATCH] rust: workqueue: Add an example for try_spawn()
Posted by Alice Ryhl 2 months ago
On Wed, Jul 30, 2025 at 09:34:39AM -0700, Boqun Feng wrote:
> `try_spawn()` could use an example to demonstrate the usage, and
> arguably it's the most simple usage of workqueue in case someone needs a
> deferred work, so add it.
> 
> Cc: Joel Fernandes <joelagnelf@nvidia.com>
> Cc: "Paul E. McKenney" <paulmck@kernel.org>
> Signed-off-by: Boqun Feng <boqun.feng@gmail.com>
> ---
> Miguel, Alice and Tejun, while I'm at it, should we also rename the
> function to `spawn()` because of the motivation mentioned here [1]?
> 
> [1]: https://rust-for-linux.zulipchat.com/#narrow/channel/291566-Library/topic/.60new.60.20or.20.60try_new.60.3F/near/529533317

Renaming sounds good to me.

> Also I find the `{ <clone> || { } }` is really good if I only need to
> clone the Arc for passing to a callback closure, but I'm not sure how
> people feel about it, so criticism is welcome ;-)

It's a neat trick, but I think it risks confusing people. I would
probably not include it in an example like this one.

>  rust/kernel/workqueue.rs | 27 +++++++++++++++++++++++++++
>  1 file changed, 27 insertions(+)
> 
> diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs
> index b9343d5bc00f..59c1a5e14d12 100644
> --- a/rust/kernel/workqueue.rs
> +++ b/rust/kernel/workqueue.rs
> @@ -331,6 +331,33 @@ pub fn enqueue_delayed<W, const ID: u64>(&self, w: W, delay: Jiffies) -> W::Enqu
>      /// 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.
> +    ///
> +    /// # Examples
> +    ///
> +    /// ```
> +    /// use kernel::{alloc::flags, sync::{Arc, Completion, new_spinlock}, workqueue};
> +    ///
> +    /// let work_done = Arc::pin_init(Completion::new(), flags::GFP_KERNEL)?;
> +    /// let data = Arc::pin_init(new_spinlock!(0), flags::GFP_KERNEL)?;
> +    ///
> +    /// workqueue::system().try_spawn(
> +    ///     flags::GFP_KERNEL,
> +    ///     {
> +    ///         let work_done = work_done.clone();
> +    ///         let data = data.clone();
> +    ///         move || {
> +    ///             *data.lock() = 42;
> +    ///             work_done.complete_all();
> +    ///         }
> +    ///     }
> +    /// )?;
> +    ///
> +    /// work_done.wait_for_completion();
> +    ///
> +    /// // `work_done` being completed implies the observation of the write of `data` in the work.
> +    /// assert_eq!(*data.lock(), 42);
> +    /// # Ok::<(), Error>(())
> +    /// ```
>      pub fn try_spawn<T: 'static + Send + FnOnce()>(
>          &self,
>          flags: Flags,
> -- 
> 2.39.5 (Apple Git-154)
>
Re: [RFC PATCH] rust: workqueue: Add an example for try_spawn()
Posted by Benno Lossin 2 months ago
On Wed Jul 30, 2025 at 6:34 PM CEST, Boqun Feng wrote:
> `try_spawn()` could use an example to demonstrate the usage, and
> arguably it's the most simple usage of workqueue in case someone needs a
> deferred work, so add it.
>
> Cc: Joel Fernandes <joelagnelf@nvidia.com>
> Cc: "Paul E. McKenney" <paulmck@kernel.org>
> Signed-off-by: Boqun Feng <boqun.feng@gmail.com>
> ---
> Miguel, Alice and Tejun, while I'm at it, should we also rename the
> function to `spawn()` because of the motivation mentioned here [1]?
>
> [1]: https://rust-for-linux.zulipchat.com/#narrow/channel/291566-Library/topic/.60new.60.20or.20.60try_new.60.3F/near/529533317
>
> Also I find the `{ <clone> || { } }` is really good if I only need to
> clone the Arc for passing to a callback closure, but I'm not sure how
> people feel about it, so criticism is welcome ;-)

I'm not so sure, see below :)

>  rust/kernel/workqueue.rs | 27 +++++++++++++++++++++++++++
>  1 file changed, 27 insertions(+)
>
> diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs
> index b9343d5bc00f..59c1a5e14d12 100644
> --- a/rust/kernel/workqueue.rs
> +++ b/rust/kernel/workqueue.rs
> @@ -331,6 +331,33 @@ pub fn enqueue_delayed<W, const ID: u64>(&self, w: W, delay: Jiffies) -> W::Enqu
>      /// 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.
> +    ///
> +    /// # Examples
> +    ///
> +    /// ```
> +    /// use kernel::{alloc::flags, sync::{Arc, Completion, new_spinlock}, workqueue};
> +    ///
> +    /// let work_done = Arc::pin_init(Completion::new(), flags::GFP_KERNEL)?;
> +    /// let data = Arc::pin_init(new_spinlock!(0), flags::GFP_KERNEL)?;
> +    ///
> +    /// workqueue::system().try_spawn(
> +    ///     flags::GFP_KERNEL,
> +    ///     {
> +    ///         let work_done = work_done.clone();
> +    ///         let data = data.clone();
> +    ///         move || {
> +    ///             *data.lock() = 42;
> +    ///             work_done.complete_all();
> +    ///         }
> +    ///     }
> +    /// )?;

Not doing your pattern and instead adding a `2` postfix we get:

    let work_done2 = work_done.clone();
    let data2 = data.clone();

    workqueue::system().try_spawn(flags::GFP_KERNEL, move || {
        *data2.lock() = 42;
        work_done2.complete_all();
    })?;

There are some discussions of introducing some better syntax for (cheap)
cloning, so maybe we can use that in the future.

---
Cheers,
Benno

> +    ///
> +    /// work_done.wait_for_completion();
> +    ///
> +    /// // `work_done` being completed implies the observation of the write of `data` in the work.
> +    /// assert_eq!(*data.lock(), 42);
> +    /// # Ok::<(), Error>(())
> +    /// ```
>      pub fn try_spawn<T: 'static + Send + FnOnce()>(
>          &self,
>          flags: Flags,
Re: [RFC PATCH] rust: workqueue: Add an example for try_spawn()
Posted by Boqun Feng 2 months ago
On Wed, Jul 30, 2025 at 09:28:05PM +0200, Benno Lossin wrote:
> On Wed Jul 30, 2025 at 6:34 PM CEST, Boqun Feng wrote:
> > `try_spawn()` could use an example to demonstrate the usage, and
> > arguably it's the most simple usage of workqueue in case someone needs a
> > deferred work, so add it.
> >
> > Cc: Joel Fernandes <joelagnelf@nvidia.com>
> > Cc: "Paul E. McKenney" <paulmck@kernel.org>
> > Signed-off-by: Boqun Feng <boqun.feng@gmail.com>
> > ---
> > Miguel, Alice and Tejun, while I'm at it, should we also rename the
> > function to `spawn()` because of the motivation mentioned here [1]?
> >
> > [1]: https://rust-for-linux.zulipchat.com/#narrow/channel/291566-Library/topic/.60new.60.20or.20.60try_new.60.3F/near/529533317
> >
> > Also I find the `{ <clone> || { } }` is really good if I only need to
> > clone the Arc for passing to a callback closure, but I'm not sure how
> > people feel about it, so criticism is welcome ;-)
> 
> I'm not so sure, see below :)
> 
> >  rust/kernel/workqueue.rs | 27 +++++++++++++++++++++++++++
> >  1 file changed, 27 insertions(+)
> >
> > diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs
> > index b9343d5bc00f..59c1a5e14d12 100644
> > --- a/rust/kernel/workqueue.rs
> > +++ b/rust/kernel/workqueue.rs
> > @@ -331,6 +331,33 @@ pub fn enqueue_delayed<W, const ID: u64>(&self, w: W, delay: Jiffies) -> W::Enqu
> >      /// 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.
> > +    ///
> > +    /// # Examples
> > +    ///
> > +    /// ```
> > +    /// use kernel::{alloc::flags, sync::{Arc, Completion, new_spinlock}, workqueue};
> > +    ///
> > +    /// let work_done = Arc::pin_init(Completion::new(), flags::GFP_KERNEL)?;
> > +    /// let data = Arc::pin_init(new_spinlock!(0), flags::GFP_KERNEL)?;
> > +    ///
> > +    /// workqueue::system().try_spawn(
> > +    ///     flags::GFP_KERNEL,
> > +    ///     {
> > +    ///         let work_done = work_done.clone();
> > +    ///         let data = data.clone();
> > +    ///         move || {
> > +    ///             *data.lock() = 42;
> > +    ///             work_done.complete_all();
> > +    ///         }
> > +    ///     }
> > +    /// )?;
> 
> Not doing your pattern and instead adding a `2` postfix we get:
> 
>     let work_done2 = work_done.clone();
>     let data2 = data.clone();
> 

Yeah, the thing I want to achieve with my pattern is: make it clear that
the work and the task that queues the work are sharing the same
`work_done` and `data` (well, no the same `Arc` exactly, but the `Arc`s
that are pointing to the same object). This pattern here doesn't show
that clearly imo.

That said, I'm not really against using `work_done2` and `data2`, just
I'm afraid that may be more confusing.

>     workqueue::system().try_spawn(flags::GFP_KERNEL, move || {
>         *data2.lock() = 42;
>         work_done2.complete_all();
>     })?;
> 
> There are some discussions of introducing some better syntax for (cheap)
> cloning, so maybe we can use that in the future.

Do you have links to these discussions.

Regards,
Boqun

> 
> ---
> Cheers,
> Benno
> 
> > +    ///
> > +    /// work_done.wait_for_completion();
> > +    ///
> > +    /// // `work_done` being completed implies the observation of the write of `data` in the work.
> > +    /// assert_eq!(*data.lock(), 42);
> > +    /// # Ok::<(), Error>(())
> > +    /// ```
> >      pub fn try_spawn<T: 'static + Send + FnOnce()>(
> >          &self,
> >          flags: Flags,
>
Re: [RFC PATCH] rust: workqueue: Add an example for try_spawn()
Posted by Benno Lossin 2 months ago
On Wed Jul 30, 2025 at 9:38 PM CEST, Boqun Feng wrote:
> On Wed, Jul 30, 2025 at 09:28:05PM +0200, Benno Lossin wrote:
>> On Wed Jul 30, 2025 at 6:34 PM CEST, Boqun Feng wrote:
>> > +    /// workqueue::system().try_spawn(
>> > +    ///     flags::GFP_KERNEL,
>> > +    ///     {
>> > +    ///         let work_done = work_done.clone();
>> > +    ///         let data = data.clone();
>> > +    ///         move || {
>> > +    ///             *data.lock() = 42;
>> > +    ///             work_done.complete_all();
>> > +    ///         }
>> > +    ///     }
>> > +    /// )?;
>> 
>> Not doing your pattern and instead adding a `2` postfix we get:
>> 
>>     let work_done2 = work_done.clone();
>>     let data2 = data.clone();
>> 
>
> Yeah, the thing I want to achieve with my pattern is: make it clear that
> the work and the task that queues the work are sharing the same
> `work_done` and `data` (well, no the same `Arc` exactly, but the `Arc`s
> that are pointing to the same object). This pattern here doesn't show
> that clearly imo.

I think it's fine, that pattern is often used for that. Not heavily
opposed to doing it your way, but I feel like the code looks a bit weird
& my instinct is to move the let bindings out (which would produce code
that doesn't compile).

> That said, I'm not really against using `work_done2` and `data2`, just
> I'm afraid that may be more confusing.

I don't think that's a problem.

>>     workqueue::system().try_spawn(flags::GFP_KERNEL, move || {
>>         *data2.lock() = 42;
>>         work_done2.complete_all();
>>     })?;
>> 
>> There are some discussions of introducing some better syntax for (cheap)
>> cloning, so maybe we can use that in the future.
>
> Do you have links to these discussions.

It's an RFC:

    https://github.com/rust-lang/rfcs/pull/368

There probably are more discussions on zulip, but I haven't read those.
The RFC also has a project goal:

    https://rust-lang.github.io/rust-project-goals/2025h2/ergonomic-rc.html

---
Cheers,
Benno
Re: [RFC PATCH] rust: workqueue: Add an example for try_spawn()
Posted by Boqun Feng 2 months ago
On Thu, Jul 31, 2025 at 11:30:10AM +0200, Benno Lossin wrote:
> On Wed Jul 30, 2025 at 9:38 PM CEST, Boqun Feng wrote:
> > On Wed, Jul 30, 2025 at 09:28:05PM +0200, Benno Lossin wrote:
> >> On Wed Jul 30, 2025 at 6:34 PM CEST, Boqun Feng wrote:
> >> > +    /// workqueue::system().try_spawn(
> >> > +    ///     flags::GFP_KERNEL,
> >> > +    ///     {
> >> > +    ///         let work_done = work_done.clone();
> >> > +    ///         let data = data.clone();
> >> > +    ///         move || {
> >> > +    ///             *data.lock() = 42;
> >> > +    ///             work_done.complete_all();
> >> > +    ///         }
> >> > +    ///     }
> >> > +    /// )?;
> >> 
> >> Not doing your pattern and instead adding a `2` postfix we get:
> >> 
> >>     let work_done2 = work_done.clone();
> >>     let data2 = data.clone();
> >> 
> >
> > Yeah, the thing I want to achieve with my pattern is: make it clear that
> > the work and the task that queues the work are sharing the same
> > `work_done` and `data` (well, no the same `Arc` exactly, but the `Arc`s
> > that are pointing to the same object). This pattern here doesn't show
> > that clearly imo.
> 
> I think it's fine, that pattern is often used for that. Not heavily
> opposed to doing it your way, but I feel like the code looks a bit weird

Ok, I will drop my style and use work_done2 and data2, because it'll be
at the general documentation, but I might keep using my pattern in other
code because it looks reasonable to me ;-)

> & my instinct is to move the let bindings out (which would produce code
> that doesn't compile).
> 
> > That said, I'm not really against using `work_done2` and `data2`, just
> > I'm afraid that may be more confusing.
> 
> I don't think that's a problem.
> 
> >>     workqueue::system().try_spawn(flags::GFP_KERNEL, move || {
> >>         *data2.lock() = 42;
> >>         work_done2.complete_all();
> >>     })?;
> >> 
> >> There are some discussions of introducing some better syntax for (cheap)
> >> cloning, so maybe we can use that in the future.
> >
> > Do you have links to these discussions.
> 
> It's an RFC:
> 
>     https://github.com/rust-lang/rfcs/pull/368
> 
> There probably are more discussions on zulip, but I haven't read those.
> The RFC also has a project goal:
> 
>     https://rust-lang.github.io/rust-project-goals/2025h2/ergonomic-rc.html

Thanks for the pointer.

Regards,
Boqun

> 
> ---
> Cheers,
> Benno
Re: [RFC PATCH] rust: workqueue: Add an example for try_spawn()
Posted by Benno Lossin 2 months ago
On Fri Aug 1, 2025 at 3:15 AM CEST, Boqun Feng wrote:
> On Thu, Jul 31, 2025 at 11:30:10AM +0200, Benno Lossin wrote:
>> On Wed Jul 30, 2025 at 9:38 PM CEST, Boqun Feng wrote:
>> > On Wed, Jul 30, 2025 at 09:28:05PM +0200, Benno Lossin wrote:
>> >> On Wed Jul 30, 2025 at 6:34 PM CEST, Boqun Feng wrote:
>> >> > +    /// workqueue::system().try_spawn(
>> >> > +    ///     flags::GFP_KERNEL,
>> >> > +    ///     {
>> >> > +    ///         let work_done = work_done.clone();
>> >> > +    ///         let data = data.clone();
>> >> > +    ///         move || {
>> >> > +    ///             *data.lock() = 42;
>> >> > +    ///             work_done.complete_all();
>> >> > +    ///         }
>> >> > +    ///     }
>> >> > +    /// )?;
>> >> 
>> >> Not doing your pattern and instead adding a `2` postfix we get:
>> >> 
>> >>     let work_done2 = work_done.clone();
>> >>     let data2 = data.clone();
>> >> 
>> >
>> > Yeah, the thing I want to achieve with my pattern is: make it clear that
>> > the work and the task that queues the work are sharing the same
>> > `work_done` and `data` (well, no the same `Arc` exactly, but the `Arc`s
>> > that are pointing to the same object). This pattern here doesn't show
>> > that clearly imo.
>> 
>> I think it's fine, that pattern is often used for that. Not heavily
>> opposed to doing it your way, but I feel like the code looks a bit weird
>
> Ok, I will drop my style and use work_done2 and data2, because it'll be
> at the general documentation, but I might keep using my pattern in other
> code because it looks reasonable to me ;-)

It is reasonable :) If you do want to use the same name for them, then I
personally think this looks better than moving the let bindings inside
of the expression:

    {
        let work_done = work_done.clone();
        let data = data.clone();
        workqueue::system().try_spawn(flags::GFP_KERNEL, move || {
            *data.lock() = 42;
            work_done.complete_all();
        })?;
    }

But that gives the `try_spawn` call extra indentation which also isn't
ideal... I'd be best for the language feature to exist :)

---
Cheers,
Benno
Re: [RFC PATCH] rust: workqueue: Add an example for try_spawn()
Posted by Joel Fernandes 2 months ago

On 7/30/2025 12:34 PM, Boqun Feng wrote:
> `try_spawn()` could use an example to demonstrate the usage, and
> arguably it's the most simple usage of workqueue in case someone needs a
> deferred work, so add it.
> 
> Cc: Joel Fernandes <joelagnelf@nvidia.com>
> Cc: "Paul E. McKenney" <paulmck@kernel.org>
> Signed-off-by: Boqun Feng <boqun.feng@gmail.com>

Makes sense, LGTM.

Reviewed-by: Joel Fernandes <joelagnelf@nvidia.com>

thanks,

 - Joel

> ---
> Miguel, Alice and Tejun, while I'm at it, should we also rename the
> function to `spawn()` because of the motivation mentioned here [1]?
> 
> [1]: https://rust-for-linux.zulipchat.com/#narrow/channel/291566-Library/topic/.60new.60.20or.20.60try_new.60.3F/near/529533317
> 
> Also I find the `{ <clone> || { } }` is really good if I only need to
> clone the Arc for passing to a callback closure, but I'm not sure how
> people feel about it, so criticism is welcome ;-)
> 
>  rust/kernel/workqueue.rs | 27 +++++++++++++++++++++++++++
>  1 file changed, 27 insertions(+)
> 
> diff --git a/rust/kernel/workqueue.rs b/rust/kernel/workqueue.rs
> index b9343d5bc00f..59c1a5e14d12 100644
> --- a/rust/kernel/workqueue.rs
> +++ b/rust/kernel/workqueue.rs
> @@ -331,6 +331,33 @@ pub fn enqueue_delayed<W, const ID: u64>(&self, w: W, delay: Jiffies) -> W::Enqu
>      /// 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.
> +    ///
> +    /// # Examples
> +    ///
> +    /// ```
> +    /// use kernel::{alloc::flags, sync::{Arc, Completion, new_spinlock}, workqueue};
> +    ///
> +    /// let work_done = Arc::pin_init(Completion::new(), flags::GFP_KERNEL)?;
> +    /// let data = Arc::pin_init(new_spinlock!(0), flags::GFP_KERNEL)?;
> +    ///
> +    /// workqueue::system().try_spawn(
> +    ///     flags::GFP_KERNEL,
> +    ///     {
> +    ///         let work_done = work_done.clone();
> +    ///         let data = data.clone();
> +    ///         move || {
> +    ///             *data.lock() = 42;
> +    ///             work_done.complete_all();
> +    ///         }
> +    ///     }
> +    /// )?;
> +    ///
> +    /// work_done.wait_for_completion();
> +    ///
> +    /// // `work_done` being completed implies the observation of the write of `data` in the work.
> +    /// assert_eq!(*data.lock(), 42);
> +    /// # Ok::<(), Error>(())
> +    /// ```
>      pub fn try_spawn<T: 'static + Send + FnOnce()>(
>          &self,
>          flags: Flags,
Re: [RFC PATCH] rust: workqueue: Add an example for try_spawn()
Posted by Miguel Ojeda 2 months ago
On Wed, Jul 30, 2025 at 6:34 PM Boqun Feng <boqun.feng@gmail.com> wrote:
>
> Miguel, Alice and Tejun, while I'm at it, should we also rename the
> function to `spawn()` because of the motivation mentioned here [1]?

Sounds good to me.

Cheers,
Miguel
Re: [RFC PATCH] rust: workqueue: Add an example for try_spawn()
Posted by Boqun Feng 2 months ago
On Wed, Jul 30, 2025 at 06:38:46PM +0200, Miguel Ojeda wrote:
> On Wed, Jul 30, 2025 at 6:34 PM Boqun Feng <boqun.feng@gmail.com> wrote:
> >
> > Miguel, Alice and Tejun, while I'm at it, should we also rename the
> > function to `spawn()` because of the motivation mentioned here [1]?
> 
> Sounds good to me.
> 

Thanks, I will add it in v2 (probably as a separate patch) if no one
shows any objection.

Regards,
Boqun

> Cheers,
> Miguel