Skip to main content

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