Who is owning the task in the sample code of the Async book?

Hi all,
I'm reading the async book in Applied: Build an Executor - Asynchronous Programming in Rust

In the following code of the Executor, I assume the while let Ok(task) = self.ready_queue.recv() will take ownership of the task from the queue. But when this loop finishes one iteration, the task is destroyed (it is sharing owned with Arc) and the loop is blocked at self.ready_queue.recv() to wait for a new task.

My question is then who is sharing owning the task to prevent it from destroying?
By reviewing the book, the possible place is the thread that runs the timer. But the timer thread only holds a ShareState which has the Waker. Then I assume the Waker is owning the Task. But I cannot figure out the whole picture with details. Many questions remain: How the waker owns the Task and the code is? Why the Waker will own(share) the task while the task seems to own the future in which the Waker is used?

Appreciate any help! If my question is not clear please let me know. I'll try my best to improve it.

impl Executor {
    fn run(&self) {
        while let Ok(task) = self.ready_queue.recv() {
            // Take the future, and if it has not yet completed (is still Some),
            // poll it in an attempt to complete it.
            let mut future_slot = task.future.lock().unwrap();
            if let Some(mut future) = future_slot.take() {
                // Create a `LocalWaker` from the task itself
                let waker = waker_ref(&task);
                let context = &mut Context::from_waker(&*waker);
                // `BoxFuture<T>` is a type alias for
                // `Pin<Box<dyn Future<Output = T> + Send + 'static>>`.
                // We can get a `Pin<&mut dyn Future + Send + 'static>`
                // from it by calling the `Pin::as_mut` method.
                if future.as_mut().poll(context).is_pending() {
                    // We're not done processing the future, so put it
                    // back in its task to be run again in the future.
                    *future_slot = Some(future);

The ArcWake trait is doing the heavy lifting here. The waker has a reference to the Arc holding the task.

If the future needs to do anything interesting with the Waker, it will have to clone it. Thanks to ArcWake, cloning the waker will also clone your task. Then when the future wants to wake the task up, it sends the task back over the channel.

You can see the messy details of how ArcWake works here though waker_ref is in waker_ref.rs instead.

1 Like

Thanks for your explanation! It makes sense now.