interrupt_ref_cell/local_key.rs
1use std::thread::LocalKey;
2
3use crate::InterruptRefCell;
4
5pub trait LocalKeyExt<T> {
6 /// Acquires a reference to the contained value.
7 ///
8 /// This will lazily initialize the value if this thread has not referenced
9 /// this key yet.
10 ///
11 /// # Panics
12 ///
13 /// Panics if the value is currently mutably borrowed.
14 ///
15 /// Panics if the key currently has its destructor running,
16 /// and it **may** panic if the destructor has previously been run for this thread.
17 ///
18 /// # Example
19 ///
20 /// ```
21 /// use interrupt_ref_cell::{InterruptRefCell, LocalKeyExt};
22 ///
23 /// thread_local! {
24 /// static X: InterruptRefCell<Vec<i32>> = InterruptRefCell::new(Vec::new());
25 /// }
26 ///
27 /// X.with_borrow(|v| assert!(v.is_empty()));
28 /// ```
29 fn with_borrow<F, R>(&'static self, f: F) -> R
30 where
31 F: FnOnce(&T) -> R;
32
33 /// Acquires a mutable reference to the contained value.
34 ///
35 /// This will lazily initialize the value if this thread has not referenced
36 /// this key yet.
37 ///
38 /// # Panics
39 ///
40 /// Panics if the value is currently borrowed.
41 ///
42 /// Panics if the key currently has its destructor running,
43 /// and it **may** panic if the destructor has previously been run for this thread.
44 ///
45 /// # Example
46 ///
47 /// ```
48 /// use interrupt_ref_cell::{InterruptRefCell, LocalKeyExt};
49 ///
50 /// thread_local! {
51 /// static X: InterruptRefCell<Vec<i32>> = InterruptRefCell::new(Vec::new());
52 /// }
53 ///
54 /// X.with_borrow_mut(|v| v.push(1));
55 ///
56 /// X.with_borrow(|v| assert_eq!(*v, vec![1]));
57 /// ```
58 fn with_borrow_mut<F, R>(&'static self, f: F) -> R
59 where
60 F: FnOnce(&mut T) -> R;
61
62 /// Sets the contained value.
63 ///
64 /// <div class="warning">This will run the lazy initializer.</div>
65 ///
66 /// Unlike [`LocalKey<RefCell<T>>::set`], this method *does* run the lazy
67 /// initializer of the thread local. The required API to avoid that is not
68 /// not public.
69 ///
70 /// # Panics
71 ///
72 /// Panics if the value is currently borrowed.
73 ///
74 /// Panics if the key currently has its destructor running,
75 /// and it **may** panic if the destructor has previously been run for this thread.
76 ///
77 /// # Examples
78 ///
79 /// ```
80 /// use interrupt_ref_cell::{InterruptRefCell, LocalKeyExt};
81 ///
82 /// thread_local! {
83 /// static X: InterruptRefCell<Vec<i32>> = InterruptRefCell::new(Vec::new());
84 /// }
85 ///
86 /// // Calling X.with() here would result in a panic.
87 ///
88 /// X.set(vec![1, 2, 3]); // But X.set() is fine, as it skips the initializer above.
89 ///
90 /// X.with_borrow(|v| assert_eq!(*v, vec![1, 2, 3]));
91 /// ```
92 fn set(&'static self, value: T);
93
94 /// Takes the contained value, leaving `Default::default()` in its place.
95 ///
96 /// This will lazily initialize the value if this thread has not referenced
97 /// this key yet.
98 ///
99 /// # Panics
100 ///
101 /// Panics if the value is currently borrowed.
102 ///
103 /// Panics if the key currently has its destructor running,
104 /// and it **may** panic if the destructor has previously been run for this thread.
105 ///
106 /// # Examples
107 ///
108 /// ```
109 /// use interrupt_ref_cell::{InterruptRefCell, LocalKeyExt};
110 ///
111 /// thread_local! {
112 /// static X: InterruptRefCell<Vec<i32>> = InterruptRefCell::new(Vec::new());
113 /// }
114 ///
115 /// X.with_borrow_mut(|v| v.push(1));
116 ///
117 /// let a = X.take();
118 ///
119 /// assert_eq!(a, vec![1]);
120 ///
121 /// X.with_borrow(|v| assert!(v.is_empty()));
122 /// ```
123 fn take(&'static self) -> T
124 where
125 T: Default;
126
127 /// Replaces the contained value, returning the old value.
128 ///
129 /// # Panics
130 ///
131 /// Panics if the value is currently borrowed.
132 ///
133 /// Panics if the key currently has its destructor running,
134 /// and it **may** panic if the destructor has previously been run for this thread.
135 ///
136 /// # Examples
137 ///
138 /// ```
139 /// use interrupt_ref_cell::{InterruptRefCell, LocalKeyExt};
140 ///
141 /// thread_local! {
142 /// static X: InterruptRefCell<Vec<i32>> = InterruptRefCell::new(Vec::new());
143 /// }
144 ///
145 /// let prev = X.replace(vec![1, 2, 3]);
146 /// assert!(prev.is_empty());
147 ///
148 /// X.with_borrow(|v| assert_eq!(*v, vec![1, 2, 3]));
149 /// ```
150 fn replace(&'static self, value: T) -> T;
151}
152
153impl<T: 'static> LocalKeyExt<T> for LocalKey<InterruptRefCell<T>> {
154 fn with_borrow<F, R>(&'static self, f: F) -> R
155 where
156 F: FnOnce(&T) -> R,
157 {
158 self.with(|cell| f(&cell.borrow()))
159 }
160
161 fn with_borrow_mut<F, R>(&'static self, f: F) -> R
162 where
163 F: FnOnce(&mut T) -> R,
164 {
165 self.with(|cell| f(&mut cell.borrow_mut()))
166 }
167
168 fn set(&'static self, value: T) {
169 // We'd rather use `RefCell::initialize_with`, which is private.
170 self.with(|cell| {
171 *cell.borrow_mut() = value;
172 });
173 }
174
175 fn take(&'static self) -> T
176 where
177 T: Default,
178 {
179 self.with(|cell| cell.take())
180 }
181
182 fn replace(&'static self, value: T) -> T {
183 self.with(|cell| cell.replace(value))
184 }
185}