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}