try_drop/handlers/fallback/
thread_local.rs1use super::{Abstracter, DefaultOnUninit};
3use crate::handlers::common::handler::CommonHandler;
4use crate::handlers::common::thread_local::scope_guard::ScopeGuard as GenericScopeGuard;
5use crate::handlers::common::thread_local::{
6 ThreadLocal as GenericThreadLocal, ThreadLocalDefinition,
7};
8use crate::handlers::common::{Fallback, ThreadLocal as ThreadLocalScope};
9use crate::handlers::on_uninit::{FlagOnUninit, PanicOnUninit};
10use crate::handlers::uninit_error::UninitializedError;
11use crate::ThreadLocalTryDropStrategy;
12use crate::TryDropStrategy;
13use anyhow::Error;
14use std::boxed::Box;
15use std::cell::{Cell, RefCell};
16use std::thread::LocalKey;
17use std::thread_local;
18
19#[cfg(feature = "ds-panic")]
20use crate::handlers::common::thread_local::DefaultThreadLocalDefinition;
21
22#[cfg(feature = "ds-panic")]
23use crate::handlers::on_uninit::UseDefaultOnUninit;
24
25pub type ThreadLocalFallbackHandler<OU = DefaultOnUninit> =
27 CommonHandler<OU, ThreadLocalScope, Fallback>;
28
29pub static DEFAULT_THREAD_LOCAL_FALLBACK_HANDLER: ThreadLocalFallbackHandler =
31 ThreadLocalFallbackHandler::DEFAULT;
32
33impl_try_drop_strategy_for!(ThreadLocalFallbackHandler where Scope: ThreadLocalScope);
34
35thread_local! {
36 static FALLBACK_HANDLER: RefCell<Option<Box<dyn ThreadLocalTryDropStrategy>>> = RefCell::new(None);
37 static LOCKED: Cell<bool> = Cell::new(false);
38}
39
40impl ThreadLocalDefinition for Fallback {
41 const UNINITIALIZED_ERROR: &'static str =
42 "the thread local fallback handler is not initialized yet";
43 const DYN: &'static str = "TryDropStrategy";
44 type ThreadLocal = Box<dyn ThreadLocalTryDropStrategy>;
45
46 fn thread_local() -> &'static LocalKey<RefCell<Option<Self::ThreadLocal>>> {
47 &FALLBACK_HANDLER
48 }
49
50 fn locked() -> &'static LocalKey<Cell<bool>> {
51 &LOCKED
52 }
53}
54
55#[cfg(feature = "ds-panic")]
56impl DefaultThreadLocalDefinition for Fallback {
57 fn default() -> Self::ThreadLocal {
58 Box::new(crate::drop_strategies::PanicDropStrategy::DEFAULT)
59 }
60}
61
62impl<T: ThreadLocalTryDropStrategy> From<T> for Box<dyn ThreadLocalTryDropStrategy> {
63 fn from(strategy: T) -> Self {
64 Box::new(strategy)
65 }
66}
67
68type ThreadLocal = GenericThreadLocal<Fallback>;
69
70pub type ScopeGuard = GenericScopeGuard<Fallback>;
73
74pub type BoxDynTryDropStrategy = Box<dyn ThreadLocalTryDropStrategy>;
76
77thread_local_methods! {
78 ThreadLocal = ThreadLocal;
79 ScopeGuard = ScopeGuard;
80 GenericStrategy = ThreadLocalTryDropStrategy;
81 DynStrategy = BoxDynTryDropStrategy;
82 feature = "ds-panic";
83
84 install;
86
87 install_dyn;
89
90 read;
95
96 try_read;
102
103 read_or_default;
108
109 write;
114
115 try_write;
121
122 write_or_default;
127
128 uninstall;
130
131 take;
133
134 replace;
137
138 replace_dyn;
141
142 scope;
144
145 scope_dyn;
148}
149
150#[cfg(test)]
151mod tests {
152 use super::*;
153 use crate::drop_strategies::{AdHocDropStrategy, AdHocFallibleDropStrategy, IntoAdHocDropStrategy, NoOpDropStrategy};
154 use crate::handlers::{fallback, primary};
155 use crate::test_utils::{ErrorsOnDrop, Fallible, FallibleDropStrategy};
156 use crate::PureTryDrop;
157 use anyhow::anyhow;
158 use std::rc::Rc;
159
160 #[test]
161 #[should_panic]
162 fn test_panic_on_uninit() {
163 ThreadLocalFallbackHandler::on_uninit_panic().handle_error(anyhow!("test"))
164 }
165
166 #[test]
167 fn test_flag_on_uninit() {
168 let handler = ThreadLocalFallbackHandler::on_uninit_flag();
169 assert!(
170 !handler.last_drop_failed(),
171 "last drop error handle failed but we haven't dropped anything yet"
172 );
173 handler.handle_error(anyhow!("test"));
174 assert!(handler.last_drop_failed(), "last drop error handle didn't fail but we have dropped something while not initialized");
175 install(NoOpDropStrategy);
176 handler.handle_error(anyhow!("test"));
177 assert!(
178 !handler.last_drop_failed(),
179 "last drop error handle failed but we have initialized"
180 );
181 }
182
183 #[test]
184 fn test_install() {
185 let installed = Rc::new(RefCell::new(false));
186 let i = Rc::clone(&installed);
187 install((move |_| *i.borrow_mut() = true).into_drop_strategy());
188 primary::thread_local::install(FallibleDropStrategy);
189 drop(ErrorsOnDrop::<Fallible, _>::not_given().adapt());
190 assert!(*installed.borrow(), "install didn't install");
191 }
192
193 #[test]
194 fn test_install_dyn() {
195 let installed = Rc::new(RefCell::new(false));
196 let i = Rc::clone(&installed);
197 install_dyn(Box::new(
198 (move |_| *i.borrow_mut() = true).into_drop_strategy(),
199 ));
200 primary::thread_local::install(FallibleDropStrategy);
201 drop(ErrorsOnDrop::<Fallible, _>::not_given().adapt());
202 assert!(*installed.borrow(), "install_dyn didn't install");
203 }
204
205 #[test]
206 #[should_panic(
207 expected = "the thread local fallback handler is not initialized yet: UninitializedError(())"
208 )]
209 fn test_read_panics_on_uninit() {
210 read(|_| panic!("did not panic on uninit"))
211 }
212
213 #[test]
214 fn test_try_read_errors_on_uninit() {
215 try_read(|_| panic!("did not error on uninit")).expect_err("did not error on uninit");
216 }
217
218 #[test]
219 fn test_read_or_default() {
220 let mut executed = false;
221 read_or_default(|_| executed = true);
222 assert!(executed, "read_or_default didn't execute");
223 }
224
225 #[test]
226 #[should_panic(
227 expected = "the thread local fallback handler is not initialized yet: UninitializedError(())"
228 )]
229 fn test_write_panics_on_uninit() {
230 write(|_| panic!("did not panic on uninit"))
231 }
232
233 #[test]
234 fn test_try_write_errors_on_uninit() {
235 try_write(|_| panic!("did not error on uninit")).expect_err("did not error on uninit");
236 }
237
238 #[test]
239 fn test_write_or_default() {
240 let mut executed = false;
241 write_or_default(|_| executed = true);
242 assert!(executed, "read_or_default didn't execute");
243 }
244 }