tokio/runtime/context/
runtime.rs

1use super::{BlockingRegionGuard, SetCurrentGuard, CONTEXT};
2
3use crate::runtime::scheduler;
4use crate::util::rand::{FastRand, RngSeed};
5
6use std::fmt;
7
8#[derive(Debug, Clone, Copy)]
9#[must_use]
10pub(crate) enum EnterRuntime {
11    /// Currently in a runtime context.
12    #[cfg_attr(not(feature = "rt"), allow(dead_code))]
13    Entered { allow_block_in_place: bool },
14
15    /// Not in a runtime context **or** a blocking region.
16    NotEntered,
17}
18
19/// Guard tracking that a caller has entered a runtime context.
20#[must_use]
21pub(crate) struct EnterRuntimeGuard {
22    /// Tracks that the current thread has entered a blocking function call.
23    pub(crate) blocking: BlockingRegionGuard,
24
25    #[allow(dead_code)] // Only tracking the guard.
26    pub(crate) handle: SetCurrentGuard,
27
28    // Tracks the previous random number generator seed
29    old_seed: RngSeed,
30}
31
32/// Marks the current thread as being within the dynamic extent of an
33/// executor.
34#[track_caller]
35pub(crate) fn enter_runtime<F, R>(handle: &scheduler::Handle, allow_block_in_place: bool, f: F) -> R
36where
37    F: FnOnce(&mut BlockingRegionGuard) -> R,
38{
39    let maybe_guard = CONTEXT.with(|c| {
40        if c.runtime.get().is_entered() {
41            None
42        } else {
43            // Set the entered flag
44            c.runtime.set(EnterRuntime::Entered {
45                allow_block_in_place,
46            });
47
48            // Generate a new seed
49            let rng_seed = handle.seed_generator().next_seed();
50
51            // Swap the RNG seed
52            let mut rng = c.rng.get().unwrap_or_else(FastRand::new);
53            let old_seed = rng.replace_seed(rng_seed);
54            c.rng.set(Some(rng));
55
56            Some(EnterRuntimeGuard {
57                blocking: BlockingRegionGuard::new(),
58                handle: c.set_current(handle),
59                old_seed,
60            })
61        }
62    });
63
64    if let Some(mut guard) = maybe_guard {
65        return f(&mut guard.blocking);
66    }
67
68    panic!(
69        "Cannot start a runtime from within a runtime. This happens \
70            because a function (like `block_on`) attempted to block the \
71            current thread while the thread is being used to drive \
72            asynchronous tasks."
73    );
74}
75
76impl fmt::Debug for EnterRuntimeGuard {
77    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78        f.debug_struct("Enter").finish()
79    }
80}
81
82impl Drop for EnterRuntimeGuard {
83    fn drop(&mut self) {
84        CONTEXT.with(|c| {
85            assert!(c.runtime.get().is_entered());
86            c.runtime.set(EnterRuntime::NotEntered);
87            // Replace the previous RNG seed
88            let mut rng = c.rng.get().unwrap_or_else(FastRand::new);
89            rng.replace_seed(self.old_seed.clone());
90            c.rng.set(Some(rng));
91        });
92    }
93}
94
95impl EnterRuntime {
96    pub(crate) fn is_entered(self) -> bool {
97        matches!(self, EnterRuntime::Entered { .. })
98    }
99}