1use super::{Context, CONTEXT};
2
3use crate::runtime::{scheduler, TryCurrentError};
4use crate::util::markers::SyncNotSend;
5
6use std::cell::{Cell, RefCell};
7use std::marker::PhantomData;
8
9#[derive(Debug)]
10#[must_use]
11pub(crate) struct SetCurrentGuard {
12 // The previous handle
13 prev: Option<scheduler::Handle>,
14
15 // The depth for this guard
16 depth: usize,
17
18 // Don't let the type move across threads.
19 _p: PhantomData<SyncNotSend>,
20}
21
22pub(super) struct HandleCell {
23 /// Current handle
24 handle: RefCell<Option<scheduler::Handle>>,
25
26 /// Tracks the number of nested calls to `try_set_current`.
27 depth: Cell<usize>,
28}
29
30/// Sets this [`Handle`] as the current active [`Handle`].
31///
32/// [`Handle`]: crate::runtime::scheduler::Handle
33pub(crate) fn try_set_current(handle: &scheduler::Handle) -> Option<SetCurrentGuard> {
34 CONTEXT.try_with(|ctx: &Context| ctx.set_current(handle)).ok()
35}
36
37pub(crate) fn with_current<F, R>(f: F) -> Result<R, TryCurrentError>
38where
39 F: FnOnce(&scheduler::Handle) -> R,
40{
41 match CONTEXT.try_with(|ctx: &Context| ctx.current.handle.borrow().as_ref().map(f)) {
42 Ok(Some(ret: R)) => Ok(ret),
43 Ok(None) => Err(TryCurrentError::new_no_context()),
44 Err(_access_error: AccessError) => Err(TryCurrentError::new_thread_local_destroyed()),
45 }
46}
47
48impl Context {
49 pub(super) fn set_current(&self, handle: &scheduler::Handle) -> SetCurrentGuard {
50 let old_handle: Option = self.current.handle.borrow_mut().replace(handle.clone());
51 let depth: usize = self.current.depth.get();
52
53 if depth == usize::MAX {
54 panic!("reached max `enter` depth");
55 }
56
57 let depth: usize = depth + 1;
58 self.current.depth.set(val:depth);
59
60 SetCurrentGuard {
61 prev: old_handle,
62 depth,
63 _p: PhantomData,
64 }
65 }
66}
67
68impl HandleCell {
69 pub(super) const fn new() -> HandleCell {
70 HandleCell {
71 handle: RefCell::new(None),
72 depth: Cell::new(0),
73 }
74 }
75}
76
77impl Drop for SetCurrentGuard {
78 fn drop(&mut self) {
79 CONTEXT.with(|ctx: &Context| {
80 let depth: usize = ctx.current.depth.get();
81
82 if depth != self.depth {
83 if !std::thread::panicking() {
84 panic!(
85 "`EnterGuard` values dropped out of order. Guards returned by \
86 `tokio::runtime::Handle::enter()` must be dropped in the reverse \
87 order as they were acquired."
88 );
89 } else {
90 // Just return... this will leave handles in a wonky state though...
91 return;
92 }
93 }
94
95 *ctx.current.handle.borrow_mut() = self.prev.take();
96 ctx.current.depth.set(val:depth - 1);
97 });
98 }
99}
100