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
    /// 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}")]
44
pub 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)]
49
enum 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]
63
pub enum StartupError {
64
    /// Task spawn failed
65
    #[error("couldn't spawn reclamation task")]
66
    Spawn(#[source] Arc<SpawnError>),
67
}
68

            
69
impl 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")]
83
pub struct TrackerCorrupted;
84

            
85
impl<T> From<PoisonError<T>> for TrackerCorrupted {
86
    fn from(_: PoisonError<T>) -> TrackerCorrupted {
87
        TrackerCorrupted
88
    }
89
}
90

            
91
impl 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)]
101
pub(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

            
111
impl MemoryReclaimedError {
112
    /// Create a new `MemoryReclaimedError` (with no additional information)
113
    pub fn new() -> Self {
114
        MemoryReclaimedError::default()
115
    }
116
}
117

            
118
impl From<Error> for MemoryReclaimedError {
119
    fn from(e: Error) -> MemoryReclaimedError {
120
        MemoryReclaimedError(e.into())
121
    }
122
}
123

            
124
impl HasKind for MemoryReclaimedError {
125
2
    fn kind(&self) -> ErrorKind {
126
2
        self.0.kind()
127
2
    }
128
}
129

            
130
impl HasKind for ReclaimedErrorInner {
131
6
    fn kind(&self) -> ErrorKind {
132
        use ErrorKind as EK;
133
        use ReclaimedErrorInner as REI;
134
6
        match self {
135
4
            REI::Collapsed => EK::LocalResourceExhausted,
136
2
            REI::TrackerError(e) => e.kind(),
137
        }
138
6
    }
139
}
140

            
141
impl HasKind for Error {
142
44
    fn kind(&self) -> ErrorKind {
143
        use Error as E;
144
        use ErrorKind as EK;
145
44
        match self {
146
12
            E::TrackerShutdown => EK::ArtiShuttingDown,
147
18
            E::AccountClosed => EK::LocalResourceExhausted,
148
10
            E::ParticipantShutdown => EK::LocalResourceExhausted,
149
2
            E::TrackerCorrupted => EK::Internal,
150
2
            E::Bug(e) => e.kind(),
151
        }
152
44
    }
153
}
154

            
155
impl HasKind for TrackerCorrupted {
156
4
    fn kind(&self) -> ErrorKind {
157
        use ErrorKind as EK;
158
4
        match self {
159
4
            TrackerCorrupted => EK::Internal,
160
4
        }
161
4
    }
162
}
163

            
164
impl HasKind for StartupError {
165
2
    fn kind(&self) -> ErrorKind {
166
        use StartupError as SE;
167
2
        match self {
168
2
            SE::Spawn(e) => e.kind(),
169
2
        }
170
2
    }
171
}
172

            
173
impl HasKind for ReclaimCrashed {
174
4
    fn kind(&self) -> ErrorKind {
175
        use ReclaimCrashed as RC;
176
4
        match self {
177
2
            RC::TrackerCorrupted(e) => e.kind(),
178
2
            RC::Bug(e) => e.kind(),
179
        }
180
4
    }
181
}
182

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