try_drop/handlers/primary/
shim.rs

1//! Manage the primary shim handler.
2
3#[cfg(feature = "ds-write")]
4mod imp {
5    use super::ShimPrimaryHandler;
6    use crate::drop_strategies::WriteDropStrategy;
7    use crate::handlers::common::handler::CommonHandler;
8    use crate::handlers::common::shim::UseDefaultOnUninitShim;
9    use crate::handlers::common::Primary;
10
11    use crate::FallibleTryDropStrategy;
12    use once_cell::sync::Lazy;
13    use std::io;
14
15    /// The default thing to do when both the global and thread-local primary handlers are
16    /// uninitialized, that is to use the internal cache.
17    pub type DefaultOnUninit = UseDefaultOnUninitShim<Primary>;
18
19    impl ShimPrimaryHandler<DefaultOnUninit> {
20        /// The default shim primary handler.
21        pub const DEFAULT: Self = Self::USE_DEFAULT_ON_UNINIT;
22    }
23
24    impl ShimPrimaryHandler<UseDefaultOnUninitShim<Primary>> {
25        /// See [`Self::use_default_on_uninit`].
26        #[allow(clippy::declare_interior_mutable_const)]
27        pub const USE_DEFAULT_ON_UNINIT: Self = Self {
28            global: CommonHandler::FLAG_ON_UNINIT,
29            thread_local: CommonHandler::FLAG_ON_UNINIT,
30            extra_data: Lazy::new(|| {
31                let mut strategy = WriteDropStrategy::stderr();
32                strategy.prelude("error: ");
33                strategy
34            }),
35        };
36
37        /// When both the global and thread-local primary handlers are uninitialized, use the
38        /// internal cache.
39        pub const fn use_default_on_uninit() -> Self {
40            Self::USE_DEFAULT_ON_UNINIT
41        }
42
43        fn cache(&self) -> &WriteDropStrategy<io::Stderr> {
44            &self.extra_data
45        }
46    }
47
48    impl FallibleTryDropStrategy for ShimPrimaryHandler<UseDefaultOnUninitShim<Primary>> {
49        type Error = anyhow::Error;
50
51        fn try_handle_error(&self, error: crate::Error) -> Result<(), Self::Error> {
52            self.on_all_uninit(error, |_, error| {
53                self.cache()
54                    .try_handle_error(error.into())
55                    .map_err(Into::into)
56            })
57        }
58    }
59}
60
61#[cfg(not(feature = "ds-write"))]
62mod imp {
63    use super::ShimPrimaryHandler;
64    use crate::handlers::on_uninit::PanicOnUninit;
65
66    /// The default thing to do when both the global and thread-local primary handlers are
67    /// uninitialized, that is to panic.
68    pub type DefaultOnUninit = PanicOnUninit;
69
70    impl ShimPrimaryHandler<DefaultOnUninit> {
71        pub const DEFAULT: Self = Self::PANIC_ON_UNINIT;
72    }
73}
74
75use crate::adapters::ArcError;
76use crate::handlers::common::handler::CommonShimHandler;
77use crate::handlers::common::shim::OnUninitShim;
78use crate::handlers::common::Primary;
79use crate::handlers::on_uninit::{DoNothingOnUninit, ErrorOnUninit, FlagOnUninit, PanicOnUninit};
80use crate::FallibleTryDropStrategy;
81pub use imp::DefaultOnUninit;
82
83/// A primary handler whose scope combines both the global and thread-local primary handlers, with
84/// the thread-local primary handler taking precedence.
85pub type ShimPrimaryHandler<OU = DefaultOnUninit> = CommonShimHandler<OU, Primary>;
86
87/// The default primary handler using the shim scope.
88pub static DEFAULT_SHIM_PRIMARY_HANDLER: ShimPrimaryHandler = ShimPrimaryHandler::DEFAULT;
89
90impl<OU: OnUninitShim> ShimPrimaryHandler<OU> {
91    fn on_all_uninit(
92        &self,
93        error: anyhow::Error,
94        f: impl FnOnce(anyhow::Error, ArcError) -> anyhow::Result<()>,
95    ) -> anyhow::Result<()> {
96        let error = ArcError::new(error);
97
98        match self
99            .thread_local
100            .try_handle_error(ArcError::clone(&error).into())
101        {
102            Ok(()) => Ok(()),
103            Err(_) if self.thread_local.last_drop_failed() => {
104                match self.global.try_handle_error(ArcError::clone(&error).into()) {
105                    Ok(()) => Ok(()),
106                    Err(uninit_error) if self.global.last_drop_failed() => f(uninit_error, error),
107                    Err(error) => Err(error),
108                }
109            }
110            Err(error) => Err(error),
111        }
112    }
113}
114
115impl FallibleTryDropStrategy for ShimPrimaryHandler<ErrorOnUninit> {
116    type Error = anyhow::Error;
117
118    fn try_handle_error(&self, error: crate::Error) -> Result<(), Self::Error> {
119        self.on_all_uninit(error, |uninit_error, _| Err(uninit_error))
120    }
121}
122
123impl FallibleTryDropStrategy for ShimPrimaryHandler<PanicOnUninit> {
124    type Error = anyhow::Error;
125
126    fn try_handle_error(&self, error: crate::Error) -> Result<(), Self::Error> {
127        self.on_all_uninit(
128            error,
129            |_, error| panic!("neither the thread local nor the global primary handlers are initialized (but here's the drop error anyway: {error})")
130        )
131    }
132}
133
134impl FallibleTryDropStrategy for ShimPrimaryHandler<DoNothingOnUninit> {
135    type Error = anyhow::Error;
136
137    fn try_handle_error(&self, error: crate::Error) -> Result<(), Self::Error> {
138        self.on_all_uninit(error, |_, _| Ok(()))
139    }
140}
141
142impl FallibleTryDropStrategy for ShimPrimaryHandler<FlagOnUninit> {
143    type Error = anyhow::Error;
144
145    fn try_handle_error(&self, error: crate::Error) -> Result<(), Self::Error> {
146        let mut last_drop_failed = false;
147        let result = self.on_all_uninit(error, |uninit_error, _| {
148            last_drop_failed = true;
149            Err(uninit_error)
150        });
151
152        self.set_last_drop_failed(last_drop_failed);
153
154        result
155    }
156}