Skip to main content

tokio/runtime/
context.rs

1use crate::loom::thread::AccessError;
2use crate::task::coop;
3
4use std::cell::Cell;
5
6#[cfg(any(feature = "rt", feature = "macros"))]
7use crate::util::rand::FastRand;
8
9cfg_rt! {
10    mod blocking;
11    pub(crate) use blocking::{disallow_block_in_place, try_enter_blocking_region, BlockingRegionGuard};
12
13    mod current;
14    pub(crate) use current::{with_current, try_set_current, SetCurrentGuard};
15
16    mod runtime;
17    pub(crate) use runtime::{EnterRuntime, enter_runtime};
18
19    mod scoped;
20    use scoped::Scoped;
21
22    use crate::runtime::{scheduler, task::Id};
23
24    use std::task::Waker;
25
26    cfg_taskdump! {
27        use crate::runtime::task::trace;
28    }
29}
30
31cfg_rt_multi_thread! {
32    mod runtime_mt;
33    pub(crate) use runtime_mt::{current_enter_context, exit_runtime};
34}
35
36struct Context {
37    /// Uniquely identifies the current thread
38    #[cfg(feature = "rt")]
39    thread_id: Cell<Option<ThreadId>>,
40
41    /// Handle to the runtime scheduler running on the current thread.
42    #[cfg(feature = "rt")]
43    current: current::HandleCell,
44
45    /// Handle to the scheduler's internal "context"
46    #[cfg(feature = "rt")]
47    scheduler: Scoped<scheduler::Context>,
48
49    #[cfg(feature = "rt")]
50    current_task_id: Cell<Option<Id>>,
51
52    /// Tracks if the current thread is currently driving a runtime.
53    /// Note, that if this is set to "entered", the current scheduler
54    /// handle may not reference the runtime currently executing. This
55    /// is because other runtime handles may be set to current from
56    /// within a runtime.
57    #[cfg(feature = "rt")]
58    runtime: Cell<EnterRuntime>,
59
60    #[cfg(any(feature = "rt", feature = "macros"))]
61    rng: Cell<Option<FastRand>>,
62
63    /// Tracks the amount of "work" a task may still do before yielding back to
64    /// the scheduler
65    budget: Cell<coop::Budget>,
66
67    #[cfg(all(
68        tokio_unstable,
69        feature = "taskdump",
70        feature = "rt",
71        target_os = "linux",
72        any(
73            target_arch = "aarch64",
74            target_arch = "x86",
75            target_arch = "x86_64",
76            target_arch = "s390x"
77        )
78    ))]
79    trace: trace::Context,
80}
81
82tokio_thread_local! {
83    static CONTEXT: Context = const {
84        Context {
85            #[cfg(feature = "rt")]
86            thread_id: Cell::new(None),
87
88            // Tracks the current runtime handle to use when spawning,
89            // accessing drivers, etc...
90            #[cfg(feature = "rt")]
91            current: current::HandleCell::new(),
92
93            // Tracks the current scheduler internal context
94            #[cfg(feature = "rt")]
95            scheduler: Scoped::new(),
96
97            #[cfg(feature = "rt")]
98            current_task_id: Cell::new(None),
99
100            // Tracks if the current thread is currently driving a runtime.
101            // Note, that if this is set to "entered", the current scheduler
102            // handle may not reference the runtime currently executing. This
103            // is because other runtime handles may be set to current from
104            // within a runtime.
105            #[cfg(feature = "rt")]
106            runtime: Cell::new(EnterRuntime::NotEntered),
107
108            #[cfg(any(feature = "rt", feature = "macros"))]
109            rng: Cell::new(None),
110
111            budget: Cell::new(coop::Budget::unconstrained()),
112
113            #[cfg(all(
114                tokio_unstable,
115                feature = "taskdump",
116                feature = "rt",
117                target_os = "linux",
118                any(
119                    target_arch = "aarch64",
120                    target_arch = "x86",
121                    target_arch = "x86_64",
122                    target_arch = "s390x"
123                )
124            ))]
125            trace: trace::Context::new(),
126        }
127    }
128}
129
130#[cfg(any(feature = "macros", all(feature = "sync", feature = "rt")))]
131pub(crate) fn thread_rng_n(n: u32) -> u32 {
132    CONTEXT.with(|ctx| {
133        let mut rng = ctx.rng.get().unwrap_or_else(FastRand::new);
134        let ret = rng.fastrand_n(n);
135        ctx.rng.set(Some(rng));
136        ret
137    })
138}
139
140pub(crate) fn budget<R>(f: impl FnOnce(&Cell<coop::Budget>) -> R) -> Result<R, AccessError> {
141    CONTEXT.try_with(|ctx| f(&ctx.budget))
142}
143
144cfg_rt! {
145    use crate::runtime::ThreadId;
146
147    pub(crate) fn thread_id() -> Result<ThreadId, AccessError> {
148        CONTEXT.try_with(|ctx| {
149            match ctx.thread_id.get() {
150                Some(id) => id,
151                None => {
152                    let id = ThreadId::next();
153                    ctx.thread_id.set(Some(id));
154                    id
155                }
156            }
157        })
158    }
159
160    pub(crate) fn set_current_task_id(id: Option<Id>) -> Option<Id> {
161        CONTEXT.try_with(|ctx| ctx.current_task_id.replace(id)).unwrap_or(None)
162    }
163
164    pub(crate) fn current_task_id() -> Option<Id> {
165        CONTEXT.try_with(|ctx| ctx.current_task_id.get()).unwrap_or(None)
166    }
167
168    #[cfg(tokio_unstable)]
169    pub(crate) fn worker_index() -> Option<usize> {
170        with_scheduler(|ctx| ctx.and_then(|c| c.worker_index()))
171    }
172
173    #[track_caller]
174    pub(crate) fn defer(waker: &Waker) {
175        with_scheduler(|maybe_scheduler| {
176            if let Some(scheduler) = maybe_scheduler {
177                scheduler.defer(waker);
178            } else {
179                // Called from outside of the runtime, immediately wake the
180                // task.
181                waker.wake_by_ref();
182            }
183        });
184    }
185
186    pub(super) fn set_scheduler<R>(v: &scheduler::Context, f: impl FnOnce() -> R) -> R {
187        CONTEXT.with(|c| c.scheduler.set(v, f))
188    }
189
190    #[track_caller]
191    pub(super) fn with_scheduler<R>(f: impl FnOnce(Option<&scheduler::Context>) -> R) -> R {
192        let mut f = Some(f);
193        CONTEXT.try_with(|c| {
194            let f = f.take().unwrap();
195            if matches!(c.runtime.get(), EnterRuntime::Entered { .. }) {
196                c.scheduler.with(f)
197            } else {
198                f(None)
199            }
200        })
201            .unwrap_or_else(|_| (f.take().unwrap())(None))
202    }
203
204    cfg_taskdump! {
205        /// SAFETY: Callers of this function must ensure that trace frames always
206        /// form a valid linked list.
207        pub(crate) unsafe fn with_trace<R>(f: impl FnOnce(&trace::Context) -> R) -> Option<R> {
208            CONTEXT.try_with(|c| f(&c.trace)).ok()
209        }
210    }
211}