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    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
206    use super::*;
207    use fmt::Display;
208
209    #[test]
210    fn error_display() {
211        fn check_value(e: impl Debug + Display + HasKind) {
212            println!("{e:?} / {e} / {:?}", e.kind());
213        }
214
215        let bug = internal!("error made for testingr");
216
217        macro_rules! check_enum { {
218            $ty:ident: // should be $ty:ty but macro_rules is too broken
219            $( $variant:ident $fields:tt; )*
220        } => {
221            for e in [ $(
222                $ty::$variant $fields,
223            )* ] {
224                check_value(e);
225            }
226            match None::<$ty> {
227                None => {}
228                $( Some($ty::$variant { .. }) => {}, )*
229            }
230        } }
231
232        check_enum! {
233            Error:
234            TrackerShutdown {};
235            AccountClosed {};
236            ChildAccountAlreadyExists {};
237            ParticipantShutdown {};
238            TrackerCorrupted {};
239            Bug(bug.clone());
240        }
241
242        check_enum! {
243            ReclaimedErrorInner:
244            Collapsed {};
245            TrackerError(Error::TrackerShutdown);
246        }
247
248        check_value(MemoryReclaimedError(ReclaimedErrorInner::Collapsed));
249
250        check_enum! {
251            StartupError:
252            Spawn(SpawnError::shutdown().into());
253        }
254
255        check_value(TrackerCorrupted);
256
257        check_enum! {
258            ReclaimCrashed:
259            TrackerCorrupted(TrackerCorrupted);
260            Bug(bug.clone());
261        }
262    }
263}