diff --git a/src/runnable.rs b/src/runnable.rs index 90af65f..0642328 100644 --- a/src/runnable.rs +++ b/src/runnable.rs @@ -822,18 +822,69 @@ impl Runnable { } /// Converts this task into a raw pointer. + /// + /// To avoid a memory leak the pointer must be converted back to a Runnable using [`Runnable::from_raw`][from_raw]. + /// + /// `into_raw` does not change the state of the [`Task`], but there is no guarantee that it will be in the same state after calling [`Runnable::from_raw`][from_raw], + /// as the corresponding [`Task`] might have been dropped or cancelled. + /// + /// # Examples + /// + /// ```rust + /// use async_task::{Runnable, spawn}; + + /// let (runnable, task) = spawn(async {}, |_| {}); + /// let runnable_pointer = runnable.into_raw(); + /// + /// unsafe { + /// // Convert back to an `Runnable` to prevent leak. + /// let runnable = Runnable::<()>::from_raw(runnable_pointer); + /// let did_poll = runnable.run(); + /// assert!(did_poll); + /// // Further calls to `Runnable::from_raw(runnable_pointer)` would be memory-unsafe. + /// } + /// // The memory was freed when `x` went out of scope above, so `runnable_pointer` is now dangling! + /// ``` + /// [from_raw]: #method.from_raw pub fn into_raw(self) -> NonNull<()> { let ptr = self.ptr; mem::forget(self); ptr } - /// Converts a raw pointer into a task. + /// Converts a raw pointer into a Runnable. /// /// # Safety - /// This method should only be used with raw pointers returned from [`into_raw`]. /// - /// [`into_raw`]: #method.into_raw + /// This method should only be used with raw pointers returned from [`Runnable::into_raw`][into_raw]. + /// It is not safe to use the provided pointer once it is passed to `from_raw`. + /// Crucially, it is unsafe to call `from_raw` multiple times with the same pointer - even if the resulting [`Runnable`] is not used - + /// as internally `async-task` uses reference counting. + /// + /// It is however safe to call [`Runnable::into_raw`][into_raw] on a [`Runnable`] created with `from_raw` or + /// after the [`Task`] associated with a given Runnable has been dropped or cancelled. + /// + /// The state of the [`Runnable`] created with `from_raw` is not specified. + /// # Examples + /// + /// ```rust + /// use async_task::{Runnable, spawn}; + + /// let (runnable, task) = spawn(async {}, |_| {}); + /// let runnable_pointer = runnable.into_raw(); + /// + /// drop(task); + /// unsafe { + /// // Convert back to an `Runnable` to prevent leak. + /// let runnable = Runnable::<()>::from_raw(runnable_pointer); + /// let did_poll = runnable.run(); + /// assert!(!did_poll); + /// // Further calls to `Runnable::from_raw(runnable_pointer)` would be memory-unsafe. + /// } + /// // The memory was freed when `x` went out of scope above, so `runnable_pointer` is now dangling! + /// ``` + + /// [into_raw]: #method.into_raw pub unsafe fn from_raw(ptr: NonNull<()>) -> Self { Self { ptr, diff --git a/tests/basic.rs b/tests/basic.rs index c749abd..c9fb9f4 100644 --- a/tests/basic.rs +++ b/tests/basic.rs @@ -311,7 +311,7 @@ fn raw() { task.run(); } - let mut task_got_executed = Arc::new(AtomicBool::new(false)); + let task_got_executed = Arc::new(AtomicBool::new(false)); let (runnable, _handle) = async_task::spawn( { let task_got_executed = task_got_executed.clone();