mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-09-07 00:00:08 +02:00
rt: add runtime::context to unify thread-locals (#5143)
This patch is the first step towards unifying all the thread-local variables spread out across Tokio. A new `Context` struct is added which will be used to replace the various thread-locals that exist today. Initially, `Context` only holds the current runtime handle and the random number generator. Further PRs will add other thread-local state. A previous PR removed `runtime::context`. At that time, `runtime::context` was used as an extra layer to access the various runtime driver handles. This version of `runtime::context` serves a different purpose (unifying all the thread-locals).
This commit is contained in:
+1
-1
@@ -497,8 +497,8 @@ cfg_rt! {
|
|||||||
pub mod runtime;
|
pub mod runtime;
|
||||||
}
|
}
|
||||||
cfg_not_rt! {
|
cfg_not_rt! {
|
||||||
// The `runtime` module is used when the IO or time driver is needed.
|
|
||||||
#[cfg(any(
|
#[cfg(any(
|
||||||
|
feature = "macros",
|
||||||
feature = "net",
|
feature = "net",
|
||||||
feature = "time",
|
feature = "time",
|
||||||
all(unix, feature = "process"),
|
all(unix, feature = "process"),
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
cfg_macros! {
|
cfg_macros! {
|
||||||
pub use crate::future::poll_fn;
|
pub use crate::future::poll_fn;
|
||||||
pub use crate::future::maybe_done::maybe_done;
|
pub use crate::future::maybe_done::maybe_done;
|
||||||
pub use crate::util::thread_rng_n;
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub fn thread_rng_n(n: u32) -> u32 {
|
||||||
|
crate::runtime::context::thread_rng_n(n)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use std::future::Future;
|
pub use std::future::Future;
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ use crate::runtime::blocking::{shutdown, BlockingTask};
|
|||||||
use crate::runtime::builder::ThreadNameFn;
|
use crate::runtime::builder::ThreadNameFn;
|
||||||
use crate::runtime::task::{self, JoinHandle};
|
use crate::runtime::task::{self, JoinHandle};
|
||||||
use crate::runtime::{Builder, Callback, Handle};
|
use crate::runtime::{Builder, Callback, Handle};
|
||||||
use crate::util::{replace_thread_rng, RngSeedGenerator};
|
|
||||||
|
|
||||||
use std::collections::{HashMap, VecDeque};
|
use std::collections::{HashMap, VecDeque};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
@@ -48,9 +47,6 @@ struct Inner {
|
|||||||
|
|
||||||
// Customizable wait timeout.
|
// Customizable wait timeout.
|
||||||
keep_alive: Duration,
|
keep_alive: Duration,
|
||||||
|
|
||||||
// Random number seed
|
|
||||||
seed_generator: RngSeedGenerator,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Shared {
|
struct Shared {
|
||||||
@@ -185,7 +181,6 @@ impl BlockingPool {
|
|||||||
before_stop: builder.before_stop.clone(),
|
before_stop: builder.before_stop.clone(),
|
||||||
thread_cap,
|
thread_cap,
|
||||||
keep_alive,
|
keep_alive,
|
||||||
seed_generator: builder.seed_generator.next_generator(),
|
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
shutdown_rx,
|
shutdown_rx,
|
||||||
@@ -435,8 +430,6 @@ impl Inner {
|
|||||||
if let Some(f) = &self.after_start {
|
if let Some(f) = &self.after_start {
|
||||||
f()
|
f()
|
||||||
}
|
}
|
||||||
// We own this thread so there is no need to replace the RngSeed once we're done.
|
|
||||||
let _ = replace_thread_rng(self.seed_generator.next_seed());
|
|
||||||
|
|
||||||
let mut shared = self.shared.lock();
|
let mut shared = self.shared.lock();
|
||||||
let mut join_on_thread = None;
|
let mut join_on_thread = None;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::runtime::handle::Handle;
|
use crate::runtime::handle::Handle;
|
||||||
use crate::runtime::{blocking, driver, Callback, Runtime};
|
use crate::runtime::{blocking, driver, Callback, Runtime};
|
||||||
use crate::util::{RngSeed, RngSeedGenerator};
|
use crate::util::rand::{RngSeed, RngSeedGenerator};
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
use crate::util::rand::{FastRand, RngSeed};
|
||||||
|
|
||||||
|
cfg_rt! {
|
||||||
|
use crate::runtime::scheduler;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Context {
|
||||||
|
/// Handle to the runtime scheduler running on the current thread.
|
||||||
|
#[cfg(feature = "rt")]
|
||||||
|
scheduler: RefCell<Option<scheduler::Handle>>,
|
||||||
|
rng: FastRand,
|
||||||
|
}
|
||||||
|
|
||||||
|
tokio_thread_local! {
|
||||||
|
static CONTEXT: Context = {
|
||||||
|
Context {
|
||||||
|
#[cfg(feature = "rt")]
|
||||||
|
scheduler: RefCell::new(None),
|
||||||
|
rng: FastRand::new(RngSeed::new()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "macros")]
|
||||||
|
pub(crate) fn thread_rng_n(n: u32) -> u32 {
|
||||||
|
CONTEXT.with(|ctx| ctx.rng.fastrand_n(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg_rt! {
|
||||||
|
use crate::runtime::TryCurrentError;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct EnterGuard {
|
||||||
|
old_handle: Option<scheduler::Handle>,
|
||||||
|
old_seed: RngSeed,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn try_current() -> Result<scheduler::Handle, TryCurrentError> {
|
||||||
|
match CONTEXT.try_with(|ctx| ctx.scheduler.borrow().clone()) {
|
||||||
|
Ok(Some(handle)) => Ok(handle),
|
||||||
|
Ok(None) => Err(TryCurrentError::new_no_context()),
|
||||||
|
Err(_access_error) => Err(TryCurrentError::new_thread_local_destroyed()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets this [`Handle`] as the current active [`Handle`].
|
||||||
|
///
|
||||||
|
/// [`Handle`]: crate::runtime::scheduler::Handle
|
||||||
|
pub(crate) fn try_enter(handle: &scheduler::Handle) -> Option<EnterGuard> {
|
||||||
|
let rng_seed = handle.seed_generator().next_seed();
|
||||||
|
|
||||||
|
CONTEXT.try_with(|ctx| {
|
||||||
|
let old_handle = ctx.scheduler.borrow_mut().replace(handle.clone());
|
||||||
|
let old_seed = ctx.rng.replace_seed(rng_seed);
|
||||||
|
|
||||||
|
EnterGuard {
|
||||||
|
old_handle,
|
||||||
|
old_seed,
|
||||||
|
}
|
||||||
|
}).ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for EnterGuard {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
CONTEXT.with(|ctx| {
|
||||||
|
*ctx.scheduler.borrow_mut() = self.old_handle.take();
|
||||||
|
ctx.rng.replace_seed(self.old_seed.clone());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::runtime::{scheduler, RuntimeFlavor};
|
use crate::runtime::{context, scheduler, RuntimeFlavor};
|
||||||
|
|
||||||
/// Handle to the runtime.
|
/// Handle to the runtime.
|
||||||
///
|
///
|
||||||
@@ -29,7 +29,7 @@ use std::{error, fmt};
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
#[must_use = "Creating and dropping a guard does nothing"]
|
#[must_use = "Creating and dropping a guard does nothing"]
|
||||||
pub struct EnterGuard<'a> {
|
pub struct EnterGuard<'a> {
|
||||||
_guard: scheduler::EnterGuard,
|
_guard: context::EnterGuard,
|
||||||
_handle_lifetime: PhantomData<&'a Handle>,
|
_handle_lifetime: PhantomData<&'a Handle>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,7 +106,7 @@ impl Handle {
|
|||||||
///
|
///
|
||||||
/// Contrary to `current`, this never panics
|
/// Contrary to `current`, this never panics
|
||||||
pub fn try_current() -> Result<Self, TryCurrentError> {
|
pub fn try_current() -> Result<Self, TryCurrentError> {
|
||||||
scheduler::Handle::try_current().map(|inner| Handle { inner })
|
context::try_current().map(|inner| Handle { inner })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Spawns a future onto the Tokio runtime.
|
/// Spawns a future onto the Tokio runtime.
|
||||||
|
|||||||
@@ -177,7 +177,11 @@
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
|
#[cfg(any(feature = "rt", feature = "macros"))]
|
||||||
|
pub(crate) mod context;
|
||||||
|
|
||||||
mod driver;
|
mod driver;
|
||||||
|
|
||||||
pub(crate) mod scheduler;
|
pub(crate) mod scheduler;
|
||||||
|
|
||||||
cfg_io_driver_impl! {
|
cfg_io_driver_impl! {
|
||||||
@@ -223,7 +227,7 @@ cfg_rt! {
|
|||||||
pub use self::builder::Builder;
|
pub use self::builder::Builder;
|
||||||
cfg_unstable! {
|
cfg_unstable! {
|
||||||
pub use self::builder::UnhandledPanic;
|
pub use self::builder::UnhandledPanic;
|
||||||
pub use crate::util::RngSeed;
|
pub use crate::util::rand::RngSeed;
|
||||||
}
|
}
|
||||||
|
|
||||||
use self::enter::enter;
|
use self::enter::enter;
|
||||||
@@ -632,7 +636,7 @@ cfg_rt! {
|
|||||||
Scheduler::CurrentThread(current_thread) => {
|
Scheduler::CurrentThread(current_thread) => {
|
||||||
// This ensures that tasks spawned on the current-thread
|
// This ensures that tasks spawned on the current-thread
|
||||||
// runtime are dropped inside the runtime's context.
|
// runtime are dropped inside the runtime's context.
|
||||||
match self.handle.inner.try_enter() {
|
match context::try_enter(&self.handle.inner) {
|
||||||
Some(guard) => current_thread.set_context_guard(guard),
|
Some(guard) => current_thread.set_context_guard(guard),
|
||||||
None => {
|
None => {
|
||||||
// The context thread-local has already been destroyed.
|
// The context thread-local has already been destroyed.
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use crate::future::poll_fn;
|
use crate::future::poll_fn;
|
||||||
use crate::loom::sync::atomic::AtomicBool;
|
use crate::loom::sync::atomic::AtomicBool;
|
||||||
use crate::loom::sync::{Arc, Mutex};
|
use crate::loom::sync::{Arc, Mutex};
|
||||||
|
use crate::runtime::context::EnterGuard;
|
||||||
use crate::runtime::driver::{self, Driver};
|
use crate::runtime::driver::{self, Driver};
|
||||||
use crate::runtime::scheduler::EnterGuard;
|
|
||||||
use crate::runtime::task::{self, JoinHandle, OwnedTasks, Schedule, Task};
|
use crate::runtime::task::{self, JoinHandle, OwnedTasks, Schedule, Task};
|
||||||
use crate::runtime::{blocking, Config};
|
use crate::runtime::{blocking, Config};
|
||||||
use crate::runtime::{MetricsBatch, SchedulerMetrics, WorkerMetrics};
|
use crate::runtime::{MetricsBatch, SchedulerMetrics, WorkerMetrics};
|
||||||
|
|||||||
@@ -44,64 +44,30 @@ impl Handle {
|
|||||||
cfg_rt! {
|
cfg_rt! {
|
||||||
use crate::future::Future;
|
use crate::future::Future;
|
||||||
use crate::loom::sync::Arc;
|
use crate::loom::sync::Arc;
|
||||||
use crate::runtime::{blocking, task::Id, TryCurrentError};
|
use crate::runtime::{blocking, task::Id};
|
||||||
|
use crate::runtime::context::{self, EnterGuard};
|
||||||
use crate::task::JoinHandle;
|
use crate::task::JoinHandle;
|
||||||
use crate::util::{replace_thread_rng, RngSeed, RngSeedGenerator};
|
use crate::util::RngSeedGenerator;
|
||||||
|
|
||||||
use std::cell::RefCell;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub(crate) struct EnterGuard {
|
|
||||||
old_handle: Option<Handle>,
|
|
||||||
old_seed: RngSeed,
|
|
||||||
}
|
|
||||||
|
|
||||||
tokio_thread_local! {
|
|
||||||
static CURRENT: RefCell<Option<Handle>> = const { RefCell::new(None) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Handle {
|
impl Handle {
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub(crate) fn current() -> Handle {
|
pub(crate) fn current() -> Handle {
|
||||||
match Handle::try_current() {
|
match context::try_current() {
|
||||||
Ok(handle) => handle,
|
Ok(handle) => handle,
|
||||||
Err(e) => panic!("{}", e),
|
Err(e) => panic!("{}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn try_current() -> Result<Handle, TryCurrentError> {
|
|
||||||
match CURRENT.try_with(|ctx| ctx.borrow().clone()) {
|
|
||||||
Ok(Some(handle)) => Ok(handle),
|
|
||||||
Ok(None) => Err(TryCurrentError::new_no_context()),
|
|
||||||
Err(_access_error) => Err(TryCurrentError::new_thread_local_destroyed()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets this [`Handle`] as the current active [`Handle`].
|
/// Sets this [`Handle`] as the current active [`Handle`].
|
||||||
///
|
///
|
||||||
/// [`Handle`]: Handle
|
/// [`Handle`]: Handle
|
||||||
pub(crate) fn enter(&self) -> EnterGuard {
|
pub(crate) fn enter(&self) -> EnterGuard {
|
||||||
match self.try_enter() {
|
match context::try_enter(self) {
|
||||||
Some(guard) => guard,
|
Some(guard) => guard,
|
||||||
None => panic!("{}", crate::util::error::THREAD_LOCAL_DESTROYED_ERROR),
|
None => panic!("{}", crate::util::error::THREAD_LOCAL_DESTROYED_ERROR),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets this [`Handle`] as the current active [`Handle`].
|
|
||||||
///
|
|
||||||
/// [`Handle`]: Handle
|
|
||||||
pub(crate) fn try_enter(&self) -> Option<EnterGuard> {
|
|
||||||
let rng_seed = self.seed_generator().next_seed();
|
|
||||||
let old_handle = CURRENT.try_with(|ctx| ctx.borrow_mut().replace(self.clone())).ok()?;
|
|
||||||
|
|
||||||
let old_seed = replace_thread_rng(rng_seed);
|
|
||||||
|
|
||||||
Some(EnterGuard {
|
|
||||||
old_handle,
|
|
||||||
old_seed,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn blocking_spawner(&self) -> &blocking::Spawner {
|
pub(crate) fn blocking_spawner(&self) -> &blocking::Spawner {
|
||||||
match self {
|
match self {
|
||||||
Handle::CurrentThread(h) => &h.blocking_spawner,
|
Handle::CurrentThread(h) => &h.blocking_spawner,
|
||||||
@@ -143,16 +109,6 @@ cfg_rt! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for EnterGuard {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
CURRENT.with(|ctx| {
|
|
||||||
*ctx.borrow_mut() = self.old_handle.take();
|
|
||||||
});
|
|
||||||
// We discard the RngSeed associated with this guard
|
|
||||||
let _ = replace_thread_rng(self.old_seed.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg_metrics! {
|
cfg_metrics! {
|
||||||
use crate::runtime::{SchedulerMetrics, WorkerMetrics};
|
use crate::runtime::{SchedulerMetrics, WorkerMetrics};
|
||||||
|
|
||||||
@@ -201,6 +157,12 @@ cfg_rt! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cfg_not_rt! {
|
cfg_not_rt! {
|
||||||
|
#[cfg(any(
|
||||||
|
feature = "net",
|
||||||
|
all(unix, feature = "process"),
|
||||||
|
all(unix, feature = "signal"),
|
||||||
|
feature = "time",
|
||||||
|
))]
|
||||||
impl Handle {
|
impl Handle {
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub(crate) fn current() -> Handle {
|
pub(crate) fn current() -> Handle {
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ use crate::runtime::{
|
|||||||
blocking, driver, task, Config, MetricsBatch, SchedulerMetrics, WorkerMetrics,
|
blocking, driver, task, Config, MetricsBatch, SchedulerMetrics, WorkerMetrics,
|
||||||
};
|
};
|
||||||
use crate::util::atomic_cell::AtomicCell;
|
use crate::util::atomic_cell::AtomicCell;
|
||||||
use crate::util::{FastRand, RngSeedGenerator};
|
use crate::util::rand::{FastRand, RngSeedGenerator};
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|||||||
+2
-12
@@ -44,13 +44,13 @@ pub(crate) use wake_list::WakeList;
|
|||||||
pub(crate) mod linked_list;
|
pub(crate) mod linked_list;
|
||||||
|
|
||||||
#[cfg(any(feature = "rt", feature = "macros"))]
|
#[cfg(any(feature = "rt", feature = "macros"))]
|
||||||
mod rand;
|
pub(crate) mod rand;
|
||||||
|
|
||||||
cfg_rt! {
|
cfg_rt! {
|
||||||
mod idle_notified_set;
|
mod idle_notified_set;
|
||||||
pub(crate) use idle_notified_set::IdleNotifiedSet;
|
pub(crate) use idle_notified_set::IdleNotifiedSet;
|
||||||
|
|
||||||
pub(crate) use self::rand::{RngSeedGenerator,replace_thread_rng};
|
pub(crate) use self::rand::RngSeedGenerator;
|
||||||
|
|
||||||
mod wake;
|
mod wake;
|
||||||
pub(crate) use wake::WakerRef;
|
pub(crate) use wake::WakerRef;
|
||||||
@@ -66,17 +66,7 @@ cfg_rt! {
|
|||||||
pub(crate) use rc_cell::RcCell;
|
pub(crate) use rc_cell::RcCell;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(not(tokio_unstable), allow(unreachable_pub))]
|
|
||||||
#[cfg(feature = "rt")]
|
|
||||||
pub use self::rand::RngSeed;
|
|
||||||
|
|
||||||
#[cfg(any(feature = "macros"))]
|
|
||||||
#[cfg_attr(not(feature = "macros"), allow(unreachable_pub))]
|
|
||||||
pub use self::rand::thread_rng_n;
|
|
||||||
|
|
||||||
cfg_rt_multi_thread! {
|
cfg_rt_multi_thread! {
|
||||||
pub(crate) use self::rand::FastRand;
|
|
||||||
|
|
||||||
mod try_lock;
|
mod try_lock;
|
||||||
pub(crate) use try_lock::TryLock;
|
pub(crate) use try_lock::TryLock;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -156,25 +156,3 @@ impl FastRand {
|
|||||||
s0.wrapping_add(s1)
|
s0.wrapping_add(s1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tokio_thread_local! {
|
|
||||||
static THREAD_RNG: FastRand = FastRand::new(RngSeed::new());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Seeds the thread local random number generator with the provided seed and
|
|
||||||
/// return the previously stored seed.
|
|
||||||
///
|
|
||||||
/// The returned seed can be later used to return the thread local random number
|
|
||||||
/// generator to its previous state.
|
|
||||||
#[cfg(feature = "rt")]
|
|
||||||
pub(crate) fn replace_thread_rng(rng_seed: RngSeed) -> RngSeed {
|
|
||||||
THREAD_RNG.with(|rng| rng.replace_seed(rng_seed))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Used by the select macro and `StreamMap`
|
|
||||||
#[cfg(any(feature = "macros"))]
|
|
||||||
#[doc(hidden)]
|
|
||||||
#[cfg_attr(not(feature = "macros"), allow(unreachable_pub))]
|
|
||||||
pub fn thread_rng_n(n: u32) -> u32 {
|
|
||||||
THREAD_RNG.with(|rng| rng.fastrand_n(n))
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user