mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-08-27 00:00:12 +02:00
fix alice's findings
This commit is contained in:
@@ -20,6 +20,8 @@ cfg_rt! {
|
|||||||
use scoped::Scoped;
|
use scoped::Scoped;
|
||||||
|
|
||||||
use crate::runtime::{scheduler, task::Id};
|
use crate::runtime::{scheduler, task::Id};
|
||||||
|
#[cfg(tokio_unstable)]
|
||||||
|
use crate::runtime::task::Header;
|
||||||
|
|
||||||
#[cfg(tokio_unstable)]
|
#[cfg(tokio_unstable)]
|
||||||
use std::ptr::NonNull;
|
use std::ptr::NonNull;
|
||||||
@@ -52,7 +54,7 @@ struct Context {
|
|||||||
current_task_id: Cell<Option<Id>>,
|
current_task_id: Cell<Option<Id>>,
|
||||||
|
|
||||||
#[cfg(all(feature = "rt", tokio_unstable))]
|
#[cfg(all(feature = "rt", tokio_unstable))]
|
||||||
current_task: Cell<Option<NonNull<()>>>,
|
current_task: Cell<Option<NonNull<Header>>>,
|
||||||
|
|
||||||
/// Tracks if the current thread is currently driving a runtime.
|
/// Tracks if the current thread is currently driving a runtime.
|
||||||
/// Note, that if this is set to "entered", the current scheduler
|
/// Note, that if this is set to "entered", the current scheduler
|
||||||
@@ -168,14 +170,28 @@ cfg_rt! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(tokio_unstable)]
|
#[cfg(tokio_unstable)]
|
||||||
pub(crate) fn set_current_task(task: Option<NonNull<()>>) -> Option<NonNull<()>> {
|
pub(crate) fn set_current_task(task: Option<NonNull<Header>>) -> Option<NonNull<Header>> {
|
||||||
CONTEXT
|
CONTEXT
|
||||||
.try_with(|ctx| ctx.current_task.replace(task))
|
.try_with(|ctx| ctx.current_task.replace(task))
|
||||||
.unwrap_or(None)
|
.unwrap_or(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(tokio_unstable)]
|
#[cfg(tokio_unstable)]
|
||||||
pub(crate) fn current_task() -> Option<NonNull<()>> {
|
pub(crate) fn set_current_task_id_and_task(
|
||||||
|
id: Option<Id>,
|
||||||
|
task: Option<NonNull<Header>>,
|
||||||
|
) -> (Option<Id>, Option<NonNull<Header>>) {
|
||||||
|
CONTEXT
|
||||||
|
.try_with(|ctx| {
|
||||||
|
let parent_task_id = ctx.current_task_id.replace(id);
|
||||||
|
let parent_task = ctx.current_task.replace(task);
|
||||||
|
(parent_task_id, parent_task)
|
||||||
|
})
|
||||||
|
.unwrap_or((None, None))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(tokio_unstable)]
|
||||||
|
pub(crate) fn current_task() -> Option<NonNull<Header>> {
|
||||||
CONTEXT
|
CONTEXT
|
||||||
.try_with(|ctx| ctx.current_task.get())
|
.try_with(|ctx| ctx.current_task.get())
|
||||||
.unwrap_or(None)
|
.unwrap_or(None)
|
||||||
|
|||||||
@@ -350,6 +350,32 @@ impl Drop for TaskIdGuard {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(tokio_unstable)]
|
||||||
|
struct TaskContextGuard {
|
||||||
|
parent_task_id: Option<Id>,
|
||||||
|
parent_task: Option<NonNull<Header>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(tokio_unstable)]
|
||||||
|
impl TaskContextGuard {
|
||||||
|
fn enter(id: Id, header: NonNull<Header>) -> Self {
|
||||||
|
let (parent_task_id, parent_task) =
|
||||||
|
context::set_current_task_id_and_task(Some(id), Some(header));
|
||||||
|
|
||||||
|
TaskContextGuard {
|
||||||
|
parent_task_id,
|
||||||
|
parent_task,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(tokio_unstable)]
|
||||||
|
impl Drop for TaskContextGuard {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
context::set_current_task_id_and_task(self.parent_task_id, self.parent_task);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: Future, S: Schedule> Core<T, S> {
|
impl<T: Future, S: Schedule> Core<T, S> {
|
||||||
/// Polls the future.
|
/// Polls the future.
|
||||||
///
|
///
|
||||||
@@ -365,12 +391,11 @@ impl<T: Future, S: Schedule> Core<T, S> {
|
|||||||
/// `self` must also be pinned. This is handled by storing the task on the
|
/// `self` must also be pinned. This is handled by storing the task on the
|
||||||
/// heap.
|
/// heap.
|
||||||
///
|
///
|
||||||
/// When `tokio_unstable` is enabled, `header` must point to the header for
|
/// `header` must point to the header for this exact task allocation, and
|
||||||
/// this exact task allocation, and the allocation must remain live until
|
/// the allocation must remain live until this function returns.
|
||||||
/// this function returns.
|
|
||||||
pub(super) unsafe fn poll(
|
pub(super) unsafe fn poll(
|
||||||
&self,
|
&self,
|
||||||
#[cfg(tokio_unstable)] header: NonNull<Header>,
|
header: NonNull<Header>,
|
||||||
mut cx: Context<'_>,
|
mut cx: Context<'_>,
|
||||||
) -> Poll<T::Output> {
|
) -> Poll<T::Output> {
|
||||||
let res = {
|
let res = {
|
||||||
@@ -384,18 +409,16 @@ impl<T: Future, S: Schedule> Core<T, S> {
|
|||||||
// Safety: The caller ensures the future is pinned.
|
// Safety: The caller ensures the future is pinned.
|
||||||
let future = unsafe { Pin::new_unchecked(future) };
|
let future = unsafe { Pin::new_unchecked(future) };
|
||||||
|
|
||||||
let _guard = TaskIdGuard::enter(self.task_id);
|
|
||||||
#[cfg(tokio_unstable)]
|
#[cfg(tokio_unstable)]
|
||||||
let _current_task = CurrentTaskGuard::enter(header);
|
let _guard = TaskContextGuard::enter(self.task_id, header);
|
||||||
|
#[cfg(not(tokio_unstable))]
|
||||||
|
let _guard = TaskIdGuard::enter(self.task_id);
|
||||||
future.poll(&mut cx)
|
future.poll(&mut cx)
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
if res.is_ready() {
|
if res.is_ready() {
|
||||||
self.drop_future_or_output(
|
self.drop_future_or_output(header);
|
||||||
#[cfg(tokio_unstable)]
|
|
||||||
header,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
res
|
res
|
||||||
@@ -406,7 +429,10 @@ impl<T: Future, S: Schedule> Core<T, S> {
|
|||||||
/// # Safety
|
/// # Safety
|
||||||
///
|
///
|
||||||
/// The caller must ensure it is safe to mutate the `stage` field.
|
/// The caller must ensure it is safe to mutate the `stage` field.
|
||||||
pub(super) fn drop_future_or_output(&self, #[cfg(tokio_unstable)] header: NonNull<Header>) {
|
pub(super) fn drop_future_or_output(&self, header: NonNull<Header>) {
|
||||||
|
#[cfg(not(tokio_unstable))]
|
||||||
|
let _ = header;
|
||||||
|
|
||||||
#[cfg(tokio_unstable)]
|
#[cfg(tokio_unstable)]
|
||||||
let _current_task = {
|
let _current_task = {
|
||||||
let dropping_future = self.stage.stage.with(|ptr| {
|
let dropping_future = self.stage.stage.with(|ptr| {
|
||||||
@@ -464,14 +490,14 @@ impl<T: Future, S: Schedule> Core<T, S> {
|
|||||||
|
|
||||||
#[cfg(tokio_unstable)]
|
#[cfg(tokio_unstable)]
|
||||||
pub(crate) struct CurrentTaskGuard {
|
pub(crate) struct CurrentTaskGuard {
|
||||||
parent_task: Option<NonNull<()>>,
|
parent_task: Option<NonNull<Header>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(tokio_unstable)]
|
#[cfg(tokio_unstable)]
|
||||||
impl CurrentTaskGuard {
|
impl CurrentTaskGuard {
|
||||||
fn enter(header: NonNull<Header>) -> Self {
|
fn enter(header: NonNull<Header>) -> Self {
|
||||||
CurrentTaskGuard {
|
CurrentTaskGuard {
|
||||||
parent_task: context::set_current_task(Some(header.cast())),
|
parent_task: context::set_current_task(Some(header)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -242,11 +242,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
if self.state().load().is_cancelled() {
|
if self.state().load().is_cancelled() {
|
||||||
cancel_task(
|
cancel_task(self.core(), header_ptr);
|
||||||
self.core(),
|
|
||||||
#[cfg(tokio_unstable)]
|
|
||||||
header_ptr,
|
|
||||||
);
|
|
||||||
return PollFuture::Complete;
|
return PollFuture::Complete;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -256,14 +252,7 @@ where
|
|||||||
// Safety: `transition_to_running` succeeded, so this thread has
|
// Safety: `transition_to_running` succeeded, so this thread has
|
||||||
// exclusive access to the future/output storage. The header pointer
|
// exclusive access to the future/output storage. The header pointer
|
||||||
// comes from this harness and remains live while the task is running.
|
// comes from this harness and remains live while the task is running.
|
||||||
let res = unsafe {
|
let res = unsafe { poll_future(self.core(), header_ptr, cx) };
|
||||||
poll_future(
|
|
||||||
self.core(),
|
|
||||||
#[cfg(tokio_unstable)]
|
|
||||||
header_ptr,
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(tokio_unstable)]
|
#[cfg(tokio_unstable)]
|
||||||
{
|
{
|
||||||
@@ -294,20 +283,12 @@ where
|
|||||||
if let TransitionToIdle::Cancelled = transition_res {
|
if let TransitionToIdle::Cancelled = transition_res {
|
||||||
// The transition to idle failed because the task was
|
// The transition to idle failed because the task was
|
||||||
// cancelled during the poll.
|
// cancelled during the poll.
|
||||||
cancel_task(
|
cancel_task(self.core(), header_ptr);
|
||||||
self.core(),
|
|
||||||
#[cfg(tokio_unstable)]
|
|
||||||
header_ptr,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
transition_result_to_poll_future(transition_res)
|
transition_result_to_poll_future(transition_res)
|
||||||
}
|
}
|
||||||
TransitionToRunning::Cancelled => {
|
TransitionToRunning::Cancelled => {
|
||||||
cancel_task(
|
cancel_task(self.core(), self.header_ptr());
|
||||||
self.core(),
|
|
||||||
#[cfg(tokio_unstable)]
|
|
||||||
self.header_ptr(),
|
|
||||||
);
|
|
||||||
PollFuture::Complete
|
PollFuture::Complete
|
||||||
}
|
}
|
||||||
TransitionToRunning::Failed => PollFuture::Done,
|
TransitionToRunning::Failed => PollFuture::Done,
|
||||||
@@ -330,11 +311,7 @@ where
|
|||||||
|
|
||||||
// By transitioning the lifecycle to `Running`, we have permission to
|
// By transitioning the lifecycle to `Running`, we have permission to
|
||||||
// drop the future.
|
// drop the future.
|
||||||
cancel_task(
|
cancel_task(self.core(), self.header_ptr());
|
||||||
self.core(),
|
|
||||||
#[cfg(tokio_unstable)]
|
|
||||||
self.header_ptr(),
|
|
||||||
);
|
|
||||||
self.complete();
|
self.complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -390,10 +367,7 @@ where
|
|||||||
// they are dropping the `JoinHandle`, we assume they are not
|
// they are dropping the `JoinHandle`, we assume they are not
|
||||||
// interested in the panic and swallow it.
|
// interested in the panic and swallow it.
|
||||||
let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
||||||
self.core().drop_future_or_output(
|
self.core().drop_future_or_output(self.header_ptr());
|
||||||
#[cfg(tokio_unstable)]
|
|
||||||
self.header_ptr(),
|
|
||||||
);
|
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -434,10 +408,7 @@ where
|
|||||||
// this task. It is our responsibility to drop the
|
// this task. It is our responsibility to drop the
|
||||||
// output. The join waker was already dropped by the
|
// output. The join waker was already dropped by the
|
||||||
// `JoinHandle` before.
|
// `JoinHandle` before.
|
||||||
self.core().drop_future_or_output(
|
self.core().drop_future_or_output(self.header_ptr());
|
||||||
#[cfg(tokio_unstable)]
|
|
||||||
self.header_ptr(),
|
|
||||||
);
|
|
||||||
} else if snapshot.is_join_waker_set() {
|
} else if snapshot.is_join_waker_set() {
|
||||||
// Notify the waker. Reading the waker field is safe per rule 4
|
// Notify the waker. Reading the waker field is safe per rule 4
|
||||||
// in task/mod.rs, since the JOIN_WAKER bit is set and the call
|
// in task/mod.rs, since the JOIN_WAKER bit is set and the call
|
||||||
@@ -592,16 +563,10 @@ enum PollFuture {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Cancels the task and store the appropriate error in the stage field.
|
/// Cancels the task and store the appropriate error in the stage field.
|
||||||
fn cancel_task<T: Future, S: Schedule>(
|
fn cancel_task<T: Future, S: Schedule>(core: &Core<T, S>, header: NonNull<Header>) {
|
||||||
core: &Core<T, S>,
|
|
||||||
#[cfg(tokio_unstable)] header: NonNull<Header>,
|
|
||||||
) {
|
|
||||||
// Drop the future from a panic guard.
|
// Drop the future from a panic guard.
|
||||||
let res = panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
let res = panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
||||||
core.drop_future_or_output(
|
core.drop_future_or_output(header);
|
||||||
#[cfg(tokio_unstable)]
|
|
||||||
header,
|
|
||||||
);
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
core.store_output(Err(panic_result_to_join_error(core.task_id, res)));
|
core.store_output(Err(panic_result_to_join_error(core.task_id, res)));
|
||||||
@@ -653,45 +618,33 @@ unsafe fn poll_hook_panic<T: Future, S: Schedule>(
|
|||||||
///
|
///
|
||||||
/// The caller must satisfy the mutual-exclusion requirements of `Core::poll`.
|
/// The caller must satisfy the mutual-exclusion requirements of `Core::poll`.
|
||||||
///
|
///
|
||||||
/// When `tokio_unstable` is enabled, `header` must point to the header for this
|
/// `header` must point to the header for this exact task allocation, and the
|
||||||
/// exact task allocation, and the allocation must remain live until this
|
/// allocation must remain live until this function returns.
|
||||||
/// function returns.
|
|
||||||
unsafe fn poll_future<T: Future, S: Schedule>(
|
unsafe fn poll_future<T: Future, S: Schedule>(
|
||||||
core: &Core<T, S>,
|
core: &Core<T, S>,
|
||||||
#[cfg(tokio_unstable)] header: NonNull<Header>,
|
header: NonNull<Header>,
|
||||||
cx: Context<'_>,
|
cx: Context<'_>,
|
||||||
) -> Poll<()> {
|
) -> Poll<()> {
|
||||||
// Poll the future.
|
// Poll the future.
|
||||||
let output = panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
let output = panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
||||||
struct Guard<'a, T: Future, S: Schedule> {
|
struct Guard<'a, T: Future, S: Schedule> {
|
||||||
core: &'a Core<T, S>,
|
core: &'a Core<T, S>,
|
||||||
#[cfg(tokio_unstable)]
|
|
||||||
header: NonNull<Header>,
|
header: NonNull<Header>,
|
||||||
}
|
}
|
||||||
impl<'a, T: Future, S: Schedule> Drop for Guard<'a, T, S> {
|
impl<'a, T: Future, S: Schedule> Drop for Guard<'a, T, S> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
// If the future panics on poll, we drop it inside the panic
|
// If the future panics on poll, we drop it inside the panic
|
||||||
// guard.
|
// guard.
|
||||||
self.core.drop_future_or_output(
|
self.core.drop_future_or_output(self.header);
|
||||||
#[cfg(tokio_unstable)]
|
|
||||||
self.header,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let guard = Guard {
|
let guard = Guard {
|
||||||
core,
|
core,
|
||||||
#[cfg(tokio_unstable)]
|
|
||||||
header,
|
header,
|
||||||
};
|
};
|
||||||
// Safety: the caller guarantees the mutual-exclusion requirements of
|
// Safety: the caller guarantees the mutual-exclusion requirements of
|
||||||
// `Core::poll` and that `header` identifies this live task allocation.
|
// `Core::poll` and that `header` identifies this live task allocation.
|
||||||
let res = unsafe {
|
let res = unsafe { guard.core.poll(header, cx) };
|
||||||
guard.core.poll(
|
|
||||||
#[cfg(tokio_unstable)]
|
|
||||||
header,
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
mem::forget(guard);
|
mem::forget(guard);
|
||||||
res
|
res
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -186,7 +186,7 @@
|
|||||||
|
|
||||||
mod core;
|
mod core;
|
||||||
use self::core::Cell;
|
use self::core::Cell;
|
||||||
use self::core::Header;
|
pub(crate) use self::core::Header;
|
||||||
|
|
||||||
mod error;
|
mod error;
|
||||||
pub use self::error::JoinError;
|
pub use self::error::JoinError;
|
||||||
@@ -687,7 +687,7 @@ pub(crate) fn with_current_task_meta<R>(
|
|||||||
|
|
||||||
// Safety: the context stores this pointer only while the referenced task is
|
// Safety: the context stores this pointer only while the referenced task is
|
||||||
// being polled, so the allocation is alive for this synchronous call.
|
// being polled, so the allocation is alive for this synchronous call.
|
||||||
let raw = unsafe { RawTask::from_raw(ptr.cast()) };
|
let raw = unsafe { RawTask::from_raw(ptr) };
|
||||||
// Safety: parent metadata is exposed read-only during this synchronous call
|
// Safety: parent metadata is exposed read-only during this synchronous call
|
||||||
// while no mutable parent hook metadata is live. The closure-bound lifetime
|
// while no mutable parent hook metadata is live. The closure-bound lifetime
|
||||||
// prevents references exposed through the metadata from escaping.
|
// prevents references exposed through the metadata from escaping.
|
||||||
|
|||||||
Reference in New Issue
Block a user