Skip to content

Is the StaticExecutor not updating the Task's reference count correctly? #140

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
songzhi opened this issue Mar 31, 2025 · 1 comment
Open

Comments

@songzhi
Copy link

songzhi commented Mar 31, 2025

This is a reproduction:

// async-task v4.4.0
impl RawTask {
  unsafe fn destory(ptr: _) {
    // ...
    // Finally, deallocate the memory reserved by the task.
    alloc::alloc::dealloc(ptr as *mut u8, task_layout.layout);
    std::eprintln!("destroyed, ptr: {:p}", ptr);
  }
}

// async-executor
#[cfg(test)]
mod tests {
    use super::*;
    use std::boxed::Box;

    #[test]
    fn task_refs() {
        let ex: &'static StaticExecutor = Box::leak(Box::new(StaticExecutor::new()));
        // let ex = Executor::new();
        {
            ex.spawn(async move {
                futures_lite::future::pending::<()>().await;
                unreachable!();
            })
            .detach();
        }

        for _ in 0..100 {
            ex.try_tick();
        }
        std::mem::forget(ex);
    }
}

Run test task_refs, we can see the destroyed, ptr: .. printed, if we use Executor, it will not print. I guess this is a bug?
The reference count of task is 1, and in the last of RawTask::run, it go through this line: https://github.com/smol-rs/async-task/blob/a11c4b22cbcbbdbc4fa0ff62bdbffb430a9d3394/src/raw.rs#L664 , so the task's memory was dealloced.

Maybe relevant PR: #112

@songzhi
Copy link
Author

songzhi commented Mar 31, 2025

    unsafe fn spawn_inner<T: 'a>(
        &self,
        future: impl Future<Output = T> + 'a,
        active: &mut Slab<Waker>,
    ) -> Task<T> {
        // Remove the task from the set of active tasks when the future finishes.
        let entry = active.vacant_entry();
        let index = entry.key();
        let state = self.state_as_arc();
        let future = AsyncCallOnDrop::new(future, move || drop(state.active().try_remove(index)));

        let (runnable, task) = Builder::new()
            .propagate_panic(true)
            .spawn_unchecked(|()| future, self.schedule());
        entry.insert(runnable.waker());

        runnable.schedule();
        task
    }

Maybe we should put this logic about reference counting into async-task's Task detaching? For example, detach a Task add a extra reference, and decrease it in extra when the future is completed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

1 participant