fumio_utils/
current.rs

1//! Generic implementation for "current" (thread-local) instances for "executor" handles.
2//!
3//! Requires a `futures_executor::Enter` reference to set.
4//!
5//! # Example
6//!
7//! ```
8//! pub struct Handle;
9//!
10//! use futures_executor::Enter;
11//! use fumio_utils::current::Current;
12//!
13//! thread_local! {
14//!     static CURRENT: Current<Handle> = Current::new();
15//! }
16//!
17//! pub fn enter<F, R>(handle: Handle, enter: &mut Enter, f: F) -> R
18//! where
19//!     F: FnOnce(&mut Enter) -> R
20//! {
21//!     Current::enter(&CURRENT, enter, handle, f)
22//! }
23//!
24//! pub fn with_current_handle<F, R>(f: F) -> R
25//! where
26//!     F: FnOnce(Option<&Handle>) -> R,
27//! {
28//!     Current::with(&CURRENT, f)
29//! }
30//!
31//! ```
32
33use futures_executor::Enter;
34use std::cell::RefCell;
35use std::thread::LocalKey;
36
37struct Reset<T: 'static> {
38	current: &'static LocalKey<Current<T>>,
39}
40
41impl<T> Drop for Reset<T> {
42	fn drop(&mut self) {
43		// ignore error
44		let _ = self.current.try_with(|c| *c.inner.borrow_mut() = None);
45	}
46}
47
48/// Holds a value when entered or nothing when not.
49#[derive(Debug)]
50pub struct Current<T> {
51	inner: RefCell<Option<T>>,
52}
53
54impl<T> Current<T> {
55	/// Construct a new (empty) instance.
56	pub const fn new() -> Self {
57		Self {
58			inner: RefCell::new(None),
59		}
60	}
61
62	/// Set instance to `value` while running the callback.
63	///
64	/// On exit the instance is cleared.
65	///
66	/// # Panics
67	///
68	/// Panics if the instance already was entered.
69	#[inline]
70	pub fn enter<F, R>(this: &'static LocalKey<Self>, enter: &mut Enter, value: T, f: F) -> R
71	where
72		F: FnOnce(&mut Enter) -> R,
73	{
74		this.with(|c| {
75			{
76				let mut inner = c.inner.borrow_mut();
77				assert!(inner.is_none(), "can't enter more than once at a time");
78				*inner = Some(value);
79			}
80			let _reset = Reset { current: this };
81			f(enter)
82		})
83	}
84
85	/// Run callback with a reference to the current value (if there is one)
86	///
87	/// The callback will be called while holding a shareable lock to the inner value.
88	///
89	/// # Panics
90	///
91	/// Panics if the inner value is currently locked exclusively by a `with_mut` call.
92	#[inline]
93	pub fn with<F, R>(this: &'static LocalKey<Self>, f: F) -> R
94	where
95		F: FnOnce(Option<&T>) -> R,
96	{
97		this.with(|c| {
98			f(c.inner.borrow().as_ref())
99		})
100	}
101
102	/// Run callback with a reference to the current value (if there is one)
103	///
104	/// The callback will be called while holding an exclusive lock to the inner value.
105	///
106	/// # Panics
107	///
108	/// Panics if the inner value is currently locked by a `with` or a `with_mut` call.
109	#[inline]
110	pub fn with_mut<F, R>(this: &'static LocalKey<Self>, f: F) -> R
111	where
112		F: FnOnce(Option<&mut T>) -> R,
113	{
114		this.with(|c| {
115			f(c.inner.borrow_mut().as_mut())
116		})
117	}
118}