diff --git a/tokio-stream/Cargo.toml b/tokio-stream/Cargo.toml index 9a90cd32c..e937ef901 100644 --- a/tokio-stream/Cargo.toml +++ b/tokio-stream/Cargo.toml @@ -37,7 +37,7 @@ signal = ["tokio/signal"] [dependencies] futures-core = { version = "0.3.0" } -pin-project-lite = "0.2.0" +pin-project-lite = "0.2.7" tokio = { version = "1.15.0", path = "../tokio", features = ["sync"] } tokio-util = { version = "0.7.0", path = "../tokio-util", optional = true } diff --git a/tokio-util/Cargo.toml b/tokio-util/Cargo.toml index b6ae0166a..4b406d818 100644 --- a/tokio-util/Cargo.toml +++ b/tokio-util/Cargo.toml @@ -40,7 +40,7 @@ futures-core = "0.3.0" futures-sink = "0.3.0" futures-io = { version = "0.3.0", optional = true } futures-util = { version = "0.3.0", optional = true } -pin-project-lite = "0.2.0" +pin-project-lite = "0.2.7" slab = { version = "0.4.4", optional = true } # Backs `DelayQueue` tracing = { version = "0.1.25", default-features = false, features = ["std"], optional = true } diff --git a/tokio/Cargo.toml b/tokio/Cargo.toml index 9656f0a4f..da7988ce1 100644 --- a/tokio/Cargo.toml +++ b/tokio/Cargo.toml @@ -99,7 +99,7 @@ autocfg = "1.1" [dependencies] tokio-macros = { version = "~2.1.0", path = "../tokio-macros", optional = true } -pin-project-lite = "0.2.0" +pin-project-lite = "0.2.7" # Everything else is optional... bytes = { version = "1.0.0", optional = true } diff --git a/tokio/src/task/task_local.rs b/tokio/src/task/task_local.rs index d3b108fe6..eeadfbd3e 100644 --- a/tokio/src/task/task_local.rs +++ b/tokio/src/task/task_local.rs @@ -1,3 +1,4 @@ +use pin_project_lite::pin_project; use std::cell::RefCell; use std::error::Error; use std::future::Future; @@ -299,36 +300,53 @@ impl fmt::Debug for LocalKey { } } -/// A future that sets a value `T` of a task local for the future `F` during -/// its execution. -/// -/// The value of the task-local must be `'static` and will be dropped on the -/// completion of the future. -/// -/// Created by the function [`LocalKey::scope`](self::LocalKey::scope). -/// -/// ### Examples -/// -/// ``` -/// # async fn dox() { -/// tokio::task_local! { -/// static NUMBER: u32; -/// } -/// -/// NUMBER.scope(1, async move { -/// println!("task local value: {}", NUMBER.get()); -/// }).await; -/// # } -/// ``` -// Doesn't use pin_project due to custom Drop. -pub struct TaskLocalFuture -where - T: 'static, -{ - local: &'static LocalKey, - slot: Option, - future: Option, - _pinned: PhantomPinned, +pin_project! { + /// A future that sets a value `T` of a task local for the future `F` during + /// its execution. + /// + /// The value of the task-local must be `'static` and will be dropped on the + /// completion of the future. + /// + /// Created by the function [`LocalKey::scope`](self::LocalKey::scope). + /// + /// ### Examples + /// + /// ``` + /// # async fn dox() { + /// tokio::task_local! { + /// static NUMBER: u32; + /// } + /// + /// NUMBER.scope(1, async move { + /// println!("task local value: {}", NUMBER.get()); + /// }).await; + /// # } + /// ``` + pub struct TaskLocalFuture + where + T: 'static, + { + local: &'static LocalKey, + slot: Option, + #[pin] + future: Option, + #[pin] + _pinned: PhantomPinned, + } + + impl PinnedDrop for TaskLocalFuture { + fn drop(this: Pin<&mut Self>) { + let this = this.project(); + if mem::needs_drop::() && this.future.is_some() { + // Drop the future while the task-local is set, if possible. Otherwise + // the future is dropped normally when the `Option` field drops. + let mut future = this.future; + let _ = this.local.scope_inner(this.slot, || { + future.set(None); + }); + } + } + } } impl Future for TaskLocalFuture { @@ -336,23 +354,21 @@ impl Future for TaskLocalFuture { #[track_caller] fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - // safety: The TaskLocalFuture struct is `!Unpin` so there is no way to - // move `self.future` from now on. - let this = unsafe { Pin::into_inner_unchecked(self) }; - let mut future_opt = unsafe { Pin::new_unchecked(&mut this.future) }; + let this = self.project(); + let mut future_opt = this.future; - let res = - this.local - .scope_inner(&mut this.slot, || match future_opt.as_mut().as_pin_mut() { - Some(fut) => { - let res = fut.poll(cx); - if res.is_ready() { - future_opt.set(None); - } - Some(res) + let res = this + .local + .scope_inner(this.slot, || match future_opt.as_mut().as_pin_mut() { + Some(fut) => { + let res = fut.poll(cx); + if res.is_ready() { + future_opt.set(None); } - None => None, - }); + Some(res) + } + None => None, + }); match res { Ok(Some(res)) => res, @@ -362,19 +378,6 @@ impl Future for TaskLocalFuture { } } -impl Drop for TaskLocalFuture { - fn drop(&mut self) { - if mem::needs_drop::() && self.future.is_some() { - // Drop the future while the task-local is set, if possible. Otherwise - // the future is dropped normally when the `Option` field drops. - let future = &mut self.future; - let _ = self.local.scope_inner(&mut self.slot, || { - *future = None; - }); - } - } -} - impl fmt::Debug for TaskLocalFuture where T: fmt::Debug,