From c7e449f5275f2ed991e1bc47d2a4005d11a33526 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Th=C3=A9riault?= Date: Wed, 5 Nov 2025 01:20:39 +0000 Subject: [PATCH 1/2] add raw metadata accessor --- src/runnable.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/runnable.rs b/src/runnable.rs index cada62d..c7659c9 100644 --- a/src/runnable.rs +++ b/src/runnable.rs @@ -720,6 +720,23 @@ impl Runnable { &self.header_with_metadata().metadata } + /// Get a pointer to the metadata associated with this task. + /// + /// Unlike [`Runnable::metadata`], this doesn't create any intermediate reference to + /// the task or the metadata, and doesn't require a reference to the task. + /// + /// # Safety + /// + /// Calling this method is always safe, but dereferencing the returned pointer isn't. + /// The pointer is only guaranteed to be valid for the scope between matching calls to + /// [`Runnable::into_raw`] and [`Runnable::from_raw`]. The returned pointer aliases the + /// references returned by [`Runnable::metadata`] and [`Task::metadata`]. + pub fn metadata_raw(ptr: NonNull<()>) -> NonNull { + let header: *mut HeaderWithMetadata = ptr.cast().as_ptr(); + // TODO: Once the MSRV reaches 1.82 this can use &raw mut instead + unsafe { NonNull::new_unchecked(core::ptr::addr_of_mut!((*header).metadata)) } + } + /// Schedules the task. /// /// This is a convenience method that passes the [`Runnable`] to the schedule function. From e829ad5355b055fe7a3531fbae2d184d200d4fe9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Th=C3=A9riault?= Date: Wed, 5 Nov 2025 02:17:35 +0000 Subject: [PATCH 2/2] add test for metadata_raw --- tests/metadata.rs | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/tests/metadata.rs b/tests/metadata.rs index d3d8d53..f61dacd 100644 --- a/tests/metadata.rs +++ b/tests/metadata.rs @@ -2,7 +2,10 @@ use async_task::{Builder, Runnable}; use flume::unbounded; use smol::future; -use std::sync::atomic::{AtomicUsize, Ordering}; +use std::{ + ptr::NonNull, + sync::atomic::{AtomicUsize, Ordering}, +}; #[test] fn metadata_use_case() { @@ -56,3 +59,43 @@ fn metadata_use_case() { t2.await; }); } + +#[test] +fn metadata_raw() { + let (sender, receiver) = unbounded::>(); + + let future = |counter: &AtomicUsize| { + assert_eq!(0, counter.fetch_add(1, Ordering::SeqCst)); + async {} + }; + + let schedule = move |runnable: Runnable| { + let ptr = runnable.into_raw(); + + { + let counter: &AtomicUsize = unsafe { Runnable::metadata_raw(ptr).as_ref() }; + assert_eq!(1, counter.fetch_add(1, Ordering::SeqCst)); + } + + sender.send(ptr).ok(); + }; + + let (runnable, task) = unsafe { + Builder::new() + .metadata(AtomicUsize::new(0)) + .spawn_unchecked(future, schedule) + }; + + runnable.schedule(); + let ptr = receiver.recv().unwrap(); + + { + let counter: &AtomicUsize = unsafe { Runnable::metadata_raw(ptr).as_ref() }; + assert_eq!(2, counter.fetch_add(1, Ordering::SeqCst)); + } + + let runnable: Runnable = unsafe { Runnable::from_raw(ptr) }; + assert_eq!(3, runnable.metadata().fetch_add(1, Ordering::SeqCst)); + + assert_eq!(4, task.metadata().load(Ordering::SeqCst)); +}