1
//! Errors arising from memory tracking
2

            
3
use crate::internal_prelude::*;
4

            
5
/// An error occurring when tracking memory usage
6
#[derive(Debug, Clone, Error)]
7
#[non_exhaustive]
8
pub 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}")]
51
pub 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)]
56
enum 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]
70
pub enum StartupError {
71
    /// Task spawn failed
72
    #[error("couldn't spawn reclamation task")]
73
    Spawn(#[source] Arc<SpawnError>),
74
}
75

            
76
impl 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")]
90
pub struct TrackerCorrupted;
91

            
92
impl<T> From<PoisonError<T>> for TrackerCorrupted {
93
    fn from(_: PoisonError<T>) -> TrackerCorrupted {
94
        TrackerCorrupted
95
    }
96
}
97

            
98
impl 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)]
108
pub(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

            
118
impl MemoryReclaimedError {
119
    /// Create a new `MemoryReclaimedError` (with no additional information)
120
    pub fn new() -> Self {
121
        MemoryReclaimedError::default()
122
    }
123
}
124

            
125
impl From<Error> for MemoryReclaimedError {
126
    fn from(e: Error) -> MemoryReclaimedError {
127
        MemoryReclaimedError(e.into())
128
    }
129
}
130

            
131
impl HasKind for MemoryReclaimedError {
132
2
    fn kind(&self) -> ErrorKind {
133
2
        self.0.kind()
134
2
    }
135
}
136

            
137
impl HasKind for ReclaimedErrorInner {
138
6
    fn kind(&self) -> ErrorKind {
139
        use ErrorKind as EK;
140
        use ReclaimedErrorInner as REI;
141
6
        match self {
142
4
            REI::Collapsed => EK::LocalResourceExhausted,
143
2
            REI::TrackerError(e) => e.kind(),
144
        }
145
6
    }
146
}
147

            
148
impl HasKind for Error {
149
46
    fn kind(&self) -> ErrorKind {
150
        use Error as E;
151
        use ErrorKind as EK;
152
46
        match self {
153
12
            E::TrackerShutdown => EK::ArtiShuttingDown,
154
18
            E::AccountClosed => EK::LocalResourceExhausted,
155
2
            E::ChildAccountAlreadyExists => EK::BadApiUsage,
156
10
            E::ParticipantShutdown => EK::LocalResourceExhausted,
157
2
            E::TrackerCorrupted => EK::Internal,
158
2
            E::Bug(e) => e.kind(),
159
        }
160
46
    }
161
}
162

            
163
impl HasKind for TrackerCorrupted {
164
4
    fn kind(&self) -> ErrorKind {
165
        use ErrorKind as EK;
166
4
        match self {
167
4
            TrackerCorrupted => EK::Internal,
168
        }
169
4
    }
170
}
171

            
172
impl HasKind for StartupError {
173
2
    fn kind(&self) -> ErrorKind {
174
        use StartupError as SE;
175
2
        match self {
176
2
            SE::Spawn(e) => e.kind(),
177
        }
178
2
    }
179
}
180

            
181
impl HasKind for ReclaimCrashed {
182
4
    fn kind(&self) -> ErrorKind {
183
        use ReclaimCrashed as RC;
184
4
        match self {
185
2
            RC::TrackerCorrupted(e) => e.kind(),
186
2
            RC::Bug(e) => e.kind(),
187
        }
188
4
    }
189
}
190

            
191
#[cfg(test)]
192
mod 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
}