| 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 | assert!(depth != usize::MAX, "reached max `enter` depth" ); |
| 54 | |
| 55 | let depth: usize = depth + 1; |
| 56 | self.current.depth.set(val:depth); |
| 57 | |
| 58 | SetCurrentGuard { |
| 59 | prev: old_handle, |
| 60 | depth, |
| 61 | _p: PhantomData, |
| 62 | } |
| 63 | } |
| 64 | } |
| 65 | |
| 66 | impl HandleCell { |
| 67 | pub(super) const fn new() -> HandleCell { |
| 68 | HandleCell { |
| 69 | handle: RefCell::new(None), |
| 70 | depth: Cell::new(0), |
| 71 | } |
| 72 | } |
| 73 | } |
| 74 | |
| 75 | impl Drop for SetCurrentGuard { |
| 76 | fn drop(&mut self) { |
| 77 | CONTEXT.with(|ctx: &Context| { |
| 78 | let depth: usize = ctx.current.depth.get(); |
| 79 | |
| 80 | if depth != self.depth { |
| 81 | if !std::thread::panicking() { |
| 82 | panic!( |
| 83 | "`EnterGuard` values dropped out of order. Guards returned by \ |
| 84 | `tokio::runtime::Handle::enter()` must be dropped in the reverse \ |
| 85 | order as they were acquired." |
| 86 | ); |
| 87 | } else { |
| 88 | // Just return... this will leave handles in a wonky state though... |
| 89 | return; |
| 90 | } |
| 91 | } |
| 92 | |
| 93 | *ctx.current.handle.borrow_mut() = self.prev.take(); |
| 94 | ctx.current.depth.set(val:depth - 1); |
| 95 | }); |
| 96 | } |
| 97 | } |
| 98 | |