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 #[cfg(feature = "rt")]
39 thread_id: Cell<Option<ThreadId>>,
40
41 #[cfg(feature = "rt")]
43 current: current::HandleCell,
44
45 #[cfg(feature = "rt")]
47 scheduler: Scoped<scheduler::Context>,
48
49 #[cfg(feature = "rt")]
50 current_task_id: Cell<Option<Id>>,
51
52 #[cfg(feature = "rt")]
58 runtime: Cell<EnterRuntime>,
59
60 #[cfg(any(feature = "rt", feature = "macros"))]
61 rng: Cell<Option<FastRand>>,
62
63 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 #[cfg(feature = "rt")]
91 current: current::HandleCell::new(),
92
93 #[cfg(feature = "rt")]
95 scheduler: Scoped::new(),
96
97 #[cfg(feature = "rt")]
98 current_task_id: Cell::new(None),
99
100 #[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", 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_default()
162 }
163
164 pub(crate) fn current_task_id() -> Option<Id> {
165 CONTEXT.try_with(|ctx| ctx.current_task_id.get()).unwrap_or_default()
166 }
167
168 pub(crate) fn worker_index() -> Option<usize> {
169 with_scheduler(|ctx| ctx.and_then(|c| c.worker_index()))
170 }
171
172 #[track_caller]
173 pub(crate) fn defer(waker: &Waker) {
174 with_scheduler(|maybe_scheduler| {
175 if let Some(scheduler) = maybe_scheduler {
176 scheduler.defer(waker);
177 } else {
178 waker.wake_by_ref();
181 }
182 });
183 }
184
185 pub(super) fn set_scheduler<R>(v: &scheduler::Context, f: impl FnOnce() -> R) -> R {
186 CONTEXT.with(|c| c.scheduler.set(v, f))
187 }
188
189 #[cfg(all(target_os = "emscripten", not(target_feature = "atomics")))]
190 pub(crate) struct ClearSchedulerGuard(*const scheduler::Context);
191
192 #[cfg(all(target_os = "emscripten", not(target_feature = "atomics")))]
193 impl Drop for ClearSchedulerGuard {
194 fn drop(&mut self) {
195 CONTEXT.with(|c| c.scheduler.inner.set(self.0));
196 }
197 }
198
199 #[cfg(all(target_os = "emscripten", not(target_feature = "atomics")))]
201 pub(crate) fn clear_scheduler() -> ClearSchedulerGuard {
202 CONTEXT.with(|c| ClearSchedulerGuard(c.scheduler.inner.replace(std::ptr::null())))
203 }
204
205 #[track_caller]
206 pub(super) fn with_scheduler<R>(f: impl FnOnce(Option<&scheduler::Context>) -> R) -> R {
207 let mut f = Some(f);
208 CONTEXT.try_with(|c| {
209 let f = f.take().unwrap();
210 if matches!(c.runtime.get(), EnterRuntime::Entered { .. }) {
211 c.scheduler.with(f)
212 } else {
213 f(None)
214 }
215 })
216 .unwrap_or_else(|_| (f.take().unwrap())(None))
217 }
218
219 cfg_taskdump! {
220 pub(crate) unsafe fn with_trace<R>(f: impl FnOnce(&trace::Context) -> R) -> Option<R> {
223 CONTEXT.try_with(|c| f(&c.trace)).ok()
224 }
225 }
226}