try_drop/handlers/fallback/
thread_local.rs

1//! Manage the thread local fallback handler.
2use 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
25/// A fallback handler that uses the thread local scope.
26pub type ThreadLocalFallbackHandler<OU = DefaultOnUninit> =
27    CommonHandler<OU, ThreadLocalScope, Fallback>;
28
29/// The default thread local fallback handler.
30pub 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
70/// A scope guard for the thread local fallback handler. This sets the thread local fallback handler
71/// to the one specified for the duration of the scope.
72pub type ScopeGuard = GenericScopeGuard<Fallback>;
73
74/// A handy type alias for `Box<dyn ThreadLocalTryDropStrategy>`.
75pub 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 a new fallback thread local handler.
85    install;
86
87    /// Install a new fallback thread local handler. Must be a dynamic trait object.
88    install_dyn;
89
90    /// Get a reference to the current fallback thread local handler.
91    ///
92    /// # Panics
93    /// If the fallback thread local handler is not initialized yet, this function will panic.
94    read;
95
96    /// Try to get a reference to the current fallback thread local handler.
97    ///
98    /// # Errors
99    /// If the fallback thread local handler is not initialized yet, this function will return an
100    /// error.
101    try_read;
102
103    /// Get a reference to the current fallback thread local handler.
104    ///
105    /// If the fallback thread local handler is not initialized yet, this will set the fallback
106    /// thread local handler to the default one.
107    read_or_default;
108
109    /// Get a mutable reference to the current fallback thread local handler.
110    ///
111    /// # Panics
112    /// If the fallback thread local handler is not initialized yet, this function will panic.
113    write;
114
115    /// Try to get a mutable reference to the current fallback thread local handler.
116    ///
117    /// # Errors
118    /// If the fallback thread local handler is not initialized yet, this function will return an
119    /// error.
120    try_write;
121
122    /// Get a mutable reference to the current fallback thread local handler.
123    ///
124    /// If the fallback thread local handler is not initialized yet, this will set the fallback
125    /// thread local handler to the default one.
126    write_or_default;
127
128    /// Uninstall the current fallback thread local handler.
129    uninstall;
130
131    /// Take the current fallback thread local handler, if there is any.
132    take;
133
134    /// Replace the current fallback thread local handler with a new one, returning the old one if
135    /// any.
136    replace;
137
138    /// Replace the current fallback thread local handler with a new one, returning the old one if
139    /// any. Must be a dynamic trait object.
140    replace_dyn;
141
142    /// Sets the fallback thread local handler to the specified one for the duration of the scope.
143    scope;
144
145    /// Sets the fallback thread local handler to the specified one for the duration of the scope.
146    /// Must be a dynamic trait object.
147    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    // todo: test uninstall, take, replace, replace_dyn, scope, scope_dyn
245}