use super::{BlockingRegionGuard, SetCurrentGuard, CONTEXT};
use crate::runtime::scheduler;
use crate::util::rand::{FastRand, RngSeed};
use std::fmt;
#[derive(Debug, Clone, Copy)]
#[must_use]
pub(crate) enum EnterRuntime {
#[cfg_attr(not(feature = "rt"), allow(dead_code))]
Entered { allow_block_in_place: bool },
NotEntered,
}
#[must_use]
pub(crate) struct EnterRuntimeGuard {
pub(crate) blocking: BlockingRegionGuard,
#[allow(dead_code)] pub(crate) handle: SetCurrentGuard,
old_seed: RngSeed,
}
#[track_caller]
pub(crate) fn enter_runtime<F, R>(handle: &scheduler::Handle, allow_block_in_place: bool, f: F) -> R
where
F: FnOnce(&mut BlockingRegionGuard) -> R,
{
let maybe_guard = CONTEXT.with(|c| {
if c.runtime.get().is_entered() {
None
} else {
c.runtime.set(EnterRuntime::Entered {
allow_block_in_place,
});
let rng_seed = handle.seed_generator().next_seed();
let mut rng = c.rng.get().unwrap_or_else(FastRand::new);
let old_seed = rng.replace_seed(rng_seed);
c.rng.set(Some(rng));
Some(EnterRuntimeGuard {
blocking: BlockingRegionGuard::new(),
handle: c.set_current(handle),
old_seed,
})
}
});
if let Some(mut guard) = maybe_guard {
return f(&mut guard.blocking);
}
panic!(
"Cannot start a runtime from within a runtime. This happens \
because a function (like `block_on`) attempted to block the \
current thread while the thread is being used to drive \
asynchronous tasks."
);
}
impl fmt::Debug for EnterRuntimeGuard {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Enter").finish()
}
}
impl Drop for EnterRuntimeGuard {
fn drop(&mut self) {
CONTEXT.with(|c| {
assert!(c.runtime.get().is_entered());
c.runtime.set(EnterRuntime::NotEntered);
let mut rng = c.rng.get().unwrap_or_else(FastRand::new);
rng.replace_seed(self.old_seed.clone());
c.rng.set(Some(rng));
});
}
}
impl EnterRuntime {
pub(crate) fn is_entered(self) -> bool {
matches!(self, EnterRuntime::Entered { .. })
}
}