1 | use super::{Context, CONTEXT}; |
2 | |
3 | use crate::runtime::{scheduler, TryCurrentError}; |
4 | use crate::util::markers::SyncNotSend; |
5 | |
6 | use std::cell::{Cell, RefCell}; |
7 | use std::marker::PhantomData; |
8 | |
9 | #[derive (Debug)] |
10 | #[must_use ] |
11 | pub(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 | |
22 | pub(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 |
33 | pub(crate) fn try_set_current(handle: &scheduler::Handle) -> Option<SetCurrentGuard> { |
34 | CONTEXT.try_with(|ctx: &Context| ctx.set_current(handle)).ok() |
35 | } |
36 | |
37 | pub(crate) fn with_current<F, R>(f: F) -> Result<R, TryCurrentError> |
38 | where |
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 | |
48 | impl 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 | |
68 | impl HandleCell { |
69 | pub(super) const fn new() -> HandleCell { |
70 | HandleCell { |
71 | handle: RefCell::new(None), |
72 | depth: Cell::new(0), |
73 | } |
74 | } |
75 | } |
76 | |
77 | impl 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 | |