Skip to content
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

task: Add TaskLocalFuture::take_value method to the task_local #6340

Merged
merged 9 commits into from
Feb 21, 2024
44 changes: 44 additions & 0 deletions tokio/src/task/task_local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,50 @@ pin_project! {
}
}

impl<T, F> TaskLocalFuture<T, F>
where
T: 'static,
{
/// Returns the value stored in the task local by this `TaskLocalFuture`.
///
/// The function returns:
///
/// * `Some(T)` if the task local value exists.
/// * `None` if the task local value has already been taken.
///
/// Note that this function attempts to take the task local value even if
/// the future has not yet completed. In that case, the value will no longer
/// be available via the task local after the call to `take_value`.
///
/// # Examples
///
/// ```
/// # async fn dox() {
/// tokio::task_local! {
/// static KEY: u32;
/// }
///
/// let fut = KEY.scope(42, async {
/// // Do some async work
/// });
///
/// let mut pinned = Box::pin(fut);
///
/// // Complete the TaskLocalFuture
/// let _ = pinned.as_mut().await;
///
/// // And here, we can take task local value
/// let value = pinned.as_mut().take_value();
///
/// assert_eq!(value, Some(42));
/// # }
/// ```
pub fn take_value(self: Pin<&mut Self>) -> Option<T> {
let this = self.project();
this.slot.take()
}
}

impl<T: 'static, F: Future> Future for TaskLocalFuture<T, F> {
type Output = F::Output;

Expand Down
28 changes: 28 additions & 0 deletions tokio/tests/task_local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,31 @@ async fn task_local_available_on_completion_drop() {
assert_eq!(rx.await.unwrap(), 42);
h.await.unwrap();
}

#[tokio::test]
async fn take_value() {
tokio::task_local! {
static KEY: u32
}
let fut = KEY.scope(1, async {});
let mut pinned = Box::pin(fut);
assert_eq!(pinned.as_mut().take_value(), Some(1));
assert_eq!(pinned.as_mut().take_value(), None);
}

#[tokio::test]
async fn poll_after_take_value_should_fail() {
tokio::task_local! {
static KEY: u32
}
let fut = KEY.scope(1, async {
let result = KEY.try_with(|_| {});
// The task local value no longer exists.
assert!(result.is_err());
});
let mut fut = Box::pin(fut);
fut.as_mut().take_value();

// Poll the future after `take_value` has been called
fut.await;
}