tor_memquota/
error.rs

1//! Errors arising from memory tracking
2
3use crate::internal_prelude::*;
4
5/// An error occurring when tracking memory usage
6#[derive(Debug, Clone, Error)]
7#[non_exhaustive]
8pub enum Error {
9    /// The memory quota tracker has been torn down
10    #[error("attempted to use shut down memory tracker")]
11    TrackerShutdown,
12
13    /// The Account has been torn down
14    ///
15    /// This can happen if the account or participant has Collapsed due to reclamation
16    #[error("memquota - attempted to use closed memory tracking account")]
17    AccountClosed,
18
19    /// The Participant has been torn down
20    ///
21    /// This can happen if the account or participant has Collapsed due to reclamation
22    #[error("memquota - attempt to allocate by torn-down memory tracking participant")]
23    ParticipantShutdown,
24
25    /// Previous bug, memory quota tracker is corrupted
26    #[error("{TrackerCorrupted}")]
27    TrackerCorrupted,
28
29    /// Bug
30    #[error("internal error")]
31    Bug(#[from] Bug),
32}
33
34/// Memory pressure means this data structure (or other facility) was torn down
35///
36/// Error type suitable for use by data structures and facilities
37/// which participate in memory tracking.
38///
39/// Convertible from a [`tor_memtrack::Error`](enum@Error),
40/// or constructible via `Default` or [`new`](MemoryReclaimedError::new).
41#[derive(Debug, Clone, Error, Default)]
42#[non_exhaustive]
43#[error("{0}")]
44pub struct MemoryReclaimedError(ReclaimedErrorInner);
45
46/// Content of a [`MemoryReclaimedError`]
47// Separate struct so we don't expose the variants
48#[derive(Debug, Clone, Error, Default)]
49enum ReclaimedErrorInner {
50    /// Collapsed, from `ReclaimedError::new`
51    #[error("data structure discarded due to memory pressure")]
52    #[default]
53    Collapsed,
54
55    /// Other error from tracker
56    #[error("{0}")]
57    TrackerError(#[from] Error),
58}
59
60/// An error occurring when setting up a memory quota tracker
61#[derive(Debug, Clone, Error)]
62#[non_exhaustive]
63pub enum StartupError {
64    /// Task spawn failed
65    #[error("couldn't spawn reclamation task")]
66    Spawn(#[source] Arc<SpawnError>),
67}
68
69impl From<SpawnError> for StartupError {
70    fn from(e: SpawnError) -> StartupError {
71        StartupError::Spawn(Arc::new(e))
72    }
73}
74
75/// Tracker corrupted
76///
77/// The memory tracker state has been corrupted.
78/// All is lost, at least as far as memory quotas are concerned.
79//
80// Separate type so we don't expose `PoisonError -> crate::Error` conversion
81#[derive(Debug, Clone, Error)]
82#[error("memory tracker is corrupted due to previous bug")]
83pub struct TrackerCorrupted;
84
85impl<T> From<PoisonError<T>> for TrackerCorrupted {
86    fn from(_: PoisonError<T>) -> TrackerCorrupted {
87        TrackerCorrupted
88    }
89}
90
91impl From<TrackerCorrupted> for Error {
92    fn from(_: TrackerCorrupted) -> Error {
93        Error::TrackerCorrupted
94    }
95}
96
97/// Error returned when reclaim task crashes
98///
99/// Does not escape the crate; is used for logging.
100#[derive(Debug, Clone, Error)]
101pub(crate) enum ReclaimCrashed {
102    /// Previous bug, memory quota tracker is corrupted
103    #[error("memory tracker corrupted due to previous bug")]
104    TrackerCorrupted(#[from] TrackerCorrupted),
105
106    /// Bug
107    #[error("internal error")]
108    Bug(#[from] Bug),
109}
110
111impl MemoryReclaimedError {
112    /// Create a new `MemoryReclaimedError` (with no additional information)
113    pub fn new() -> Self {
114        MemoryReclaimedError::default()
115    }
116}
117
118impl From<Error> for MemoryReclaimedError {
119    fn from(e: Error) -> MemoryReclaimedError {
120        MemoryReclaimedError(e.into())
121    }
122}
123
124impl HasKind for MemoryReclaimedError {
125    fn kind(&self) -> ErrorKind {
126        self.0.kind()
127    }
128}
129
130impl HasKind for ReclaimedErrorInner {
131    fn kind(&self) -> ErrorKind {
132        use ErrorKind as EK;
133        use ReclaimedErrorInner as REI;
134        match self {
135            REI::Collapsed => EK::LocalResourceExhausted,
136            REI::TrackerError(e) => e.kind(),
137        }
138    }
139}
140
141impl HasKind for Error {
142    fn kind(&self) -> ErrorKind {
143        use Error as E;
144        use ErrorKind as EK;
145        match self {
146            E::TrackerShutdown => EK::ArtiShuttingDown,
147            E::AccountClosed => EK::LocalResourceExhausted,
148            E::ParticipantShutdown => EK::LocalResourceExhausted,
149            E::TrackerCorrupted => EK::Internal,
150            E::Bug(e) => e.kind(),
151        }
152    }
153}
154
155impl HasKind for TrackerCorrupted {
156    fn kind(&self) -> ErrorKind {
157        use ErrorKind as EK;
158        match self {
159            TrackerCorrupted => EK::Internal,
160        }
161    }
162}
163
164impl HasKind for StartupError {
165    fn kind(&self) -> ErrorKind {
166        use StartupError as SE;
167        match self {
168            SE::Spawn(e) => e.kind(),
169        }
170    }
171}
172
173impl HasKind for ReclaimCrashed {
174    fn kind(&self) -> ErrorKind {
175        use ReclaimCrashed as RC;
176        match self {
177            RC::TrackerCorrupted(e) => e.kind(),
178            RC::Bug(e) => e.kind(),
179        }
180    }
181}
182
183#[cfg(test)]
184mod test {
185    // @@ begin test lint list maintained by maint/add_warning @@
186    #![allow(clippy::bool_assert_comparison)]
187    #![allow(clippy::clone_on_copy)]
188    #![allow(clippy::dbg_macro)]
189    #![allow(clippy::mixed_attributes_style)]
190    #![allow(clippy::print_stderr)]
191    #![allow(clippy::print_stdout)]
192    #![allow(clippy::single_char_pattern)]
193    #![allow(clippy::unwrap_used)]
194    #![allow(clippy::unchecked_duration_subtraction)]
195    #![allow(clippy::useless_vec)]
196    #![allow(clippy::needless_pass_by_value)]
197    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
198    use super::*;
199    use fmt::Display;
200
201    #[test]
202    fn error_display() {
203        fn check_value(e: impl Debug + Display + HasKind) {
204            println!("{e:?} / {e} / {:?}", e.kind());
205        }
206
207        let bug = internal!("error made for testingr");
208
209        macro_rules! check_enum { {
210            $ty:ident: // should be $ty:ty but macro_rules is too broken
211            $( $variant:ident $fields:tt; )*
212        } => {
213            for e in [ $(
214                $ty::$variant $fields,
215            )* ] {
216                check_value(e);
217            }
218            match None::<$ty> {
219                None => {}
220                $( Some($ty::$variant { .. }) => {}, )*
221            }
222        } }
223
224        check_enum! {
225            Error:
226            TrackerShutdown {};
227            AccountClosed {};
228            ParticipantShutdown {};
229            TrackerCorrupted {};
230            Bug(bug.clone());
231        }
232
233        check_enum! {
234            ReclaimedErrorInner:
235            Collapsed {};
236            TrackerError(Error::TrackerShutdown);
237        }
238
239        check_value(MemoryReclaimedError(ReclaimedErrorInner::Collapsed));
240
241        check_enum! {
242            StartupError:
243            Spawn(SpawnError::shutdown().into());
244        }
245
246        check_value(TrackerCorrupted);
247
248        check_enum! {
249            ReclaimCrashed:
250            TrackerCorrupted(TrackerCorrupted);
251            Bug(bug.clone());
252        }
253    }
254}