tor_memquota/
lib.rs

1#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg))]
2#![doc = include_str!("../README.md")]
3#![cfg_attr(not(feature = "memquota"), allow(unused))]
4
5//! ## Intended behaviour
6//!
7//! In normal operation we try to track as little state as possible, cheaply.
8//! We do track total memory use in nominal bytes
9//! (but a little approximately).
10//!
11//! When we exceed the quota, we engage a more expensive algorithm:
12//! we build a heap to select oldest victims, and
13//! we use the heap to keep reducing memory
14//! until we go below a low-water mark (hysteresis).
15//!
16//! ## Key concepts
17//!
18//!  * **Tracker**:
19//!    Instance of the memory quota system
20//!    Each tracker has a notion of how much memory its participants
21//!    are allowed to use, in aggregate.
22//!    Tracks memory usage by all the Accounts and Participants.
23//!    Different Trackers are completely independent.
24//!
25//!  * **Account**:
26//!    all memory used within the same Account is treated equally,
27//!    and reclamation also happens on an account-by-account basis.
28//!    (Each Account is with one Tracker.)
29//!
30//!    See [the `mq_queue` docs](mq_queue/index.html#use-in-arti)
31//!    for we use Accounts in Arti to track memory for the various queues.
32//!
33//!  * **Participant**:
34//!    one data structure that uses memory.
35//!    Each Participant is linked to *one* Account.  An account has *one or more* Participants.
36//!    (An Account can exist with zero Participants, but can't then claim memory.)
37//!    A Participant provides a `dyn IsParticipant` to the memory system;
38//!    in turn, the memory system provides the Participant with a `Participation` -
39//!    a handle for tracking memory alloc/free.
40//!
41//!    Actual memory allocation is handled by the participant itself,
42//!    using the global heap:
43//!    for each allocation, the Participant *both*
44//!    calls [`claim`](mtracker::Participation::claim)
45//!    *and* allocates the actual object,
46//!    and later, *both* frees the actual object *and*
47//!    calls [`release`](mtracker::Participation::release).
48//!
49//!  * **Child Account**/**Parent Account**:
50//!    An Account may have a Parent.
51//!    When a tracker requests memory reclamation from a Parent,
52//!    it will also request it of all that Parent's Children (but not vice versa).
53//!
54//!    The account structure and reclamation strategy for Arti is defined in
55//!    `tor-proto`, and documented in `tor_proto::memquota`.
56//!
57//!  * **Data age**:
58//!    Each Participant must be able to say what the oldest data is, that it is storing.
59//!    The reclamation policy is to try to free the oldest data.
60//!
61//!  * **Reclamation**:
62//!    When a Tracker decides that too much memory is being used,
63//!    it will select a victim Account based on the data age.
64//!    It will then ask *every Participant* in that Account,
65//!    and every Participant in every Child of that Account,
66//!    to reclaim memory.
67//!    A Participant responds by freeing at least some memory,
68//!    according to the reclamation request, and tells the Tracker when it has done so.
69//!
70//!  * **Reclamation strategy**:
71//!    To avoid too-frequent reclamation, once reclamation has started,
72//!    it will continue until a low-water mark is reached, significantly lower than the quota.
73//!    I.e. the system has a hysteresis.
74//!
75//!    The only currently implemented higher-level Participant is
76//!    [`mq_queue`], a queue which responds to a reclamation request
77//!    by completely destroying itself, freeing all its data,
78//!    and reporting it has been closed.
79//!
80//!  * <div id="is-approximate">
81//!
82//!    **Approximate** (both in time and space):
83//!    The memory quota system is not completely precise.
84//!    Participants need not report their use precisely,
85//!    but the errors should be reasonably small, and bounded.
86//!    Likewise, the enforcement is not precise:
87//!    reclamation may start slightly too early, or too late;
88//!    but the memory use will be bounded below by O(number of participants)
89//!    and above by O(1) (plus errors from the participants).
90//!    Reclamation is not immediate, and is dependent on task scheduling;
91//!    during memory pressure the quota may be exceeded;
92//!    new allocations are not prevented while attempts at reclamation are ongoing.
93//!
94//!    </div>
95//!
96// TODO we haven't implemented the queue wrapper yet
97// !  * **Queues**:
98// !    We provide a higher-level API that wraps an mpsc queue and turns it into a Participant.
99// !
100//! ## Ownership and Arc keeping-alive
101//!
102//!  * Somewhere, someone must keep an `Account` to keep the account open.
103//!    Ie, the principal object corresponding to the accountholder should contain an `Account`.
104//!
105//!  * `Arc<MemoryTracker>` holds `Weak<dyn IsParticipant>`.
106//!    If the tracker finds the `IsParticipant` has vanished,
107//!    it assumes this means that the Participant is being destroyed and
108//!    it can treat all of the memory it claimed as freed.
109//!
110//!  * Each participant holds a `Participation`.
111//!    A `Participation` may be invalidated by collapse of the underlying Account,
112//!    which may be triggered in any number of ways.
113//!
114//!  * A `Participation` does *not* keep its `Account` alive.
115//!    Ie, it has only a weak reference to the Account.
116//!
117//!  * A Participant's implementor of `IsParticipant` may hold a `Participation`.
118//!    If the `impl IsParticipant` is also the principal accountholder object,
119//!    it must hold an `Account` too.
120//!
121//!  * Child/parent accounts do not imply any keeping-alive relationship.
122//!    It's just that a reclamation request to a parent (if it still exists)
123//!    will also be made to its children.
124//!
125//!
126//! ```text
127//!     accountholder   =======================================>*  Participant
128//!                                                                (impl IsParticipant)
129//!           ||
130//!           ||                                                     ^     ||
131//!           ||                                                     |     ||
132//!           ||                 global                     Weak<dyn>|     ||
133//!           ||                     ||                              |     ||
134//!           \/*                    \/                              |     ||
135//!                                                                  |     ||
136//!         Account  *===========>  MemoryTracker  ------------------'     ||
137//!                                                                        ||
138//!            ^                                                           ||
139//!            |                                                           \/
140//!            |
141//!             `-------------------------------------------------*   Participation
142//!
143//!
144//!
145//!     accountholder which is also directly the Participant ==============\
146//!     (impl IsParticipant)                                              ||
147//!                                           ^                           ||
148//!           ||                              |                           ||
149//!           ||                              |                           ||
150//!           ||                 global       |Weak<dyn>                  ||
151//!           ||                     ||       |                           ||
152//!           \/                     \/       |                           ||
153//!                                                                       ||
154//!         Account  *===========>  MemoryTracker                         ||
155//!                                                                       ||
156//!            ^                                                          ||
157//!            |                                                          \/
158//!            |
159//!             `-------------------------------------------------*   Participation
160//!
161//! ```
162//!
163//! ## Panics and state corruption
164//!
165//! This library is intended to be entirely panic-free,
166//! even in the case of accounting errors, arithmetic overflow, etc.
167//!
168//! In the case of sufficiently bad account errors,
169//! a Participant, or a whole Account, or the whole MemoryQuotaTracker,
170//! may become unusable,
171//! in which case methods will return errors with kind [`tor_error::ErrorKind::Internal`].
172//
173// TODO MEMQUOTA: We ought to account for the fixed overhead of each stream, circuit, and
174// channel.  For example, DataWriterImpl holds a substantial fixed-length buffer.  A
175// complication is that we want to know the "data age", which is possibly the time this stream
176// was last used.
177
178// @@ begin lint list maintained by maint/add_warning @@
179#![allow(renamed_and_removed_lints)] // @@REMOVE_WHEN(ci_arti_stable)
180#![allow(unknown_lints)] // @@REMOVE_WHEN(ci_arti_nightly)
181#![warn(missing_docs)]
182#![warn(noop_method_call)]
183#![warn(unreachable_pub)]
184#![warn(clippy::all)]
185#![deny(clippy::await_holding_lock)]
186#![deny(clippy::cargo_common_metadata)]
187#![deny(clippy::cast_lossless)]
188#![deny(clippy::checked_conversions)]
189#![warn(clippy::cognitive_complexity)]
190#![deny(clippy::debug_assert_with_mut_call)]
191#![deny(clippy::exhaustive_enums)]
192#![deny(clippy::exhaustive_structs)]
193#![deny(clippy::expl_impl_clone_on_copy)]
194#![deny(clippy::fallible_impl_from)]
195#![deny(clippy::implicit_clone)]
196#![deny(clippy::large_stack_arrays)]
197#![warn(clippy::manual_ok_or)]
198#![deny(clippy::missing_docs_in_private_items)]
199#![warn(clippy::needless_borrow)]
200#![warn(clippy::needless_pass_by_value)]
201#![warn(clippy::option_option)]
202#![deny(clippy::print_stderr)]
203#![deny(clippy::print_stdout)]
204#![warn(clippy::rc_buffer)]
205#![deny(clippy::ref_option_ref)]
206#![warn(clippy::semicolon_if_nothing_returned)]
207#![warn(clippy::trait_duplication_in_bounds)]
208#![deny(clippy::unchecked_duration_subtraction)]
209#![deny(clippy::unnecessary_wraps)]
210#![warn(clippy::unseparated_literal_suffix)]
211#![deny(clippy::unwrap_used)]
212#![deny(clippy::mod_module_files)]
213#![allow(clippy::let_unit_value)] // This can reasonably be done for explicitness
214#![allow(clippy::uninlined_format_args)]
215#![allow(clippy::significant_drop_in_scrutinee)] // arti/-/merge_requests/588/#note_2812945
216#![allow(clippy::result_large_err)] // temporary workaround for arti#587
217#![allow(clippy::needless_raw_string_hashes)] // complained-about code is fine, often best
218#![allow(clippy::needless_lifetimes)] // See arti#1765
219//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
220
221// TODO #1176
222#![allow(clippy::blocks_in_conditions)]
223//
224// See `Panics` in the crate-level docs, above.
225//
226// This lint sometimes has bugs, but it seems to DTRT for me as of 1.81.0-beta.6.
227// If it breaks, these bug(s) may be relevant:
228// https://github.com/rust-lang/rust-clippy/issues/11220
229// https://github.com/rust-lang/rust-clippy/issues/11145
230// https://github.com/rust-lang/rust-clippy/issues/10209
231#![warn(clippy::arithmetic_side_effects)]
232
233// Internal supporting modules
234#[macro_use]
235mod drop_bomb;
236#[macro_use]
237mod refcount;
238#[macro_use]
239mod memory_cost_derive;
240
241mod drop_reentrancy;
242mod if_enabled;
243mod internal_prelude;
244mod utils;
245
246// Modules with public items
247mod config;
248mod error;
249pub mod memory_cost;
250pub mod mq_queue;
251pub mod mtracker;
252
253/// For trait sealing
254mod private {
255    /// Inaccessible trait
256    pub trait Sealed {}
257}
258
259/// Names exported for testing
260#[cfg(feature = "testing")]
261pub mod testing {
262    use super::*;
263    pub use config::ConfigInner;
264}
265
266//---------- re-exports at the crate root ----------
267
268pub use config::{Config, ConfigBuilder};
269pub use error::{Error, MemoryReclaimedError, StartupError};
270pub use if_enabled::EnabledToken;
271pub use memory_cost::HasMemoryCost;
272pub use memory_cost_derive::{assert_copy_static, HasMemoryCostStructural};
273pub use mtracker::{Account, MemoryQuotaTracker};
274pub use utils::ArcMemoryQuotaTrackerExt;
275
276#[doc(hidden)]
277pub use derive_deftly;
278
279/// `Result` whose `Err` is [`tor_memtrack::Error`](Error)
280pub type Result<T> = std::result::Result<T, Error>;