tor_proto/memquota.rs
1//! Types to support memory quota tracking
2//!
3//! We make these newtypes because we otherwise have a confusing a maze of
4//! identical-looking, but supposedly semantically different, [`Account`]s.
5//!
6//! # Memory tracking architecture in Arti
7//!
8//! ## Queues
9//!
10//! The following queues in Arti participate in the memory quota system:
11//!
12//! * Tor streams ([`StreamAccount`])
13//! - inbound data, on its way from the circuit to the stream's user
14//! - outbound data, on its way from the stream's user to the circuit
15//! * Tor circuits ([`CircuitAccount`])
16//! - inbound stream requests, on their way from the circuit to the handling code
17//! - inbound data, on its way from the channel
18//! * Tor channels ([`ChannelAccount`])
19//! - outbound data, on its way from a circuit to the channel
20//! (this ought to be accounted to the circuit, TODO #1652)
21//!
22//! The following data buffers do *not* participate:
23//!
24//! * Our TLS implementation(s) may have internal buffers.
25//! We hope that these buffers will be kept reasonably small,
26//! and hooking into them would in any case going be quite hard.
27//!
28//! * TCP sockets will also buffer data, in the operating system.
29//! Hooking into this is not trivial.
30//!
31//! * Our pluggable transport driver can buffer some data.
32//! This should be kept to a minimum for several reasons,
33//! so we hope that the buffers are small.
34//!
35//! * The actual pluggable transport might buffer data.
36//! Again, this should be kept to a minimum.
37//!
38//! ## Overview
39//!
40//! See the [tor_memquota] crate-level docs for an overview of the memquota system.
41//! To summarise:
42//!
43//! When too much memory is in use, the queue with the oldest data is selected for reclaim.
44//! The whole Account relating to the victim queue is torn down.
45//! When the victim Account collapses, all its queues collapse too:
46//! reading ends give EOF, and writing ends give errors.
47//! This will tear down the associated Tor protocol association.
48//!
49//! All the children Accounts of the victim Account are torn down too.
50//! This propagates the collapse to dependent Tor protocol associations.
51//!
52//! ## Accounting
53//!
54//! Within Arti we maintain a hierarchy of [`Account`]s.
55//! These are wrapped in newtypes, here in `tor_proto::memquota`.
56//!
57//! * [`ToplevelAccount`]:
58//! In a single Arti instance there will be one of these,
59//! used for all memory tracking.
60//! This is held (shared) by the chanmgr and the circmgr.
61//! Unlike the other layer-specific accounts,
62//! this is just a type alias for [`MemoryQuotaTracker`].
63//! It doesn't support claiming memory directly from it, so it won't be subject to reclaim.
64//!
65//! * [`ChannelAccount`].
66//! Contains (via parentage) everything that goes via a particular Channel.
67//! This includes all circuits on the channel, and those circuits' streams.
68//!
69//! * [`CircuitAccount`].
70//! Has the `ChannelAccount` as its parent.
71//! So if a queue accounted to a channel is selected for reclaim,
72//! that channel, and all of its circuits, will collapse.
73//!
74//! * [`StreamAccount`].
75//! Has the `CircuitAccount` as its parent.
76//! So if a queue accounted to a circuit is selected for reclaim,
77//! that circuit, and all of its streams, will collapse.
78//! If a stream's queue is selected for reclaim, only that stream will collapse.
79//! (See [#1661](https://gitlab.torproject.org/tpo/core/arti/-/issues/1661)
80//! for discussion of this behaviour.)
81//!
82//! Thus, killing a single queue will reclaim the memory associated with several other queues.
83
84use derive_deftly::{define_derive_deftly, Deftly};
85use std::sync::Arc;
86use tor_memquota::{Account, MemoryQuotaTracker};
87
88/// An [`Account`], whose type indicates which layer of the stack it's for
89//
90// Making this a trait rather than ad-hoc output from the derive macro
91// makes things more regular, and the documentation easier.
92pub trait SpecificAccount: Sized {
93 /// The type that this `Account` can be constructed from.
94 ///
95 /// The progenitor [`Account`], or, for a standalone account type,
96 /// [`Arc<MemoryQuotaTracker>`](tor_memquota::MemoryQuotaTracker).
97 type ConstructedFrom;
98
99 /// Create a new Account at this layer, given the progenitor
100 fn new(progenitor: &Self::ConstructedFrom) -> Result<Self, tor_memquota::Error>;
101
102 /// Access the underlying raw [`Account`]
103 ///
104 /// Use this when you need to actually track memory,
105 /// for example when constructing a queue with [`tor_memquota::mq_queue`]
106 fn as_raw_account(&self) -> &Account;
107
108 /// Wrap an `Account`, blessing it with a layer
109 ///
110 /// Generally, don't call this function.
111 /// Instead, use `new()`(SpecificAccount::new).
112 fn from_raw_account(account: Account) -> Self;
113
114 /// Unwrap this into a raw [`Account`]
115 fn into_raw_account(self) -> Account;
116
117 /// Create a new dummy account for testing purposes
118 fn new_noop() -> Self {
119 Self::from_raw_account(Account::new_noop())
120 }
121}
122
123define_derive_deftly! {
124 /// Implements [`SpecificAccount`]
125 ///
126 /// Exactly one of the following attributes must be supplied:
127 ///
128 /// * **`#[deftly(account_newtype(toplevel)]`**:
129 /// Standalone Account, without a parent Account.
130 /// `type ConstructedFrom = Arc<MemoryQuotaTracker>`.
131 ///
132 /// * **`#[deftly(account_newtype(parent = "PARENT_ACCOUNT"))]`**:
133 /// `type ConstructedFrom = PARENT_ACCOUNT`
134 /// (and PARENT_ACCOUNT must itself impl `SpecificAccount`).
135 ///
136 /// Applicable to newtype tuple structs, containing an [`Account`], only.
137 export SpecificAccount for struct, expect items:
138
139 ${define ACCOUNT { $crate::tor_memquota::Account }}
140
141 ${defcond HAS_PARENT tmeta(account_newtype(parent))}
142 ${defcond IS_TOPLEVEL tmeta(account_newtype(toplevel))}
143
144 ${define CONSTRUCTED_FROM {
145 ${select1
146 HAS_PARENT { ${tmeta(account_newtype(parent)) as ty} }
147 IS_TOPLEVEL { std::sync::Arc<$crate::tor_memquota::MemoryQuotaTracker> }
148 }
149 }}
150
151 impl SpecificAccount for $ttype {
152 type ConstructedFrom = $CONSTRUCTED_FROM;
153
154 fn new(src: &Self::ConstructedFrom) -> Result<Self, tor_memquota::Error> {
155 ${select1
156 HAS_PARENT { $crate::memquota::SpecificAccount::as_raw_account(src).new_child() }
157 IS_TOPLEVEL { src.new_account(None) }
158 }
159 .map(Self::from_raw_account)
160 }
161
162 fn as_raw_account(&self) -> &$ACCOUNT {
163 &self.0
164 }
165 fn from_raw_account(account: $ACCOUNT) -> Self {
166 Self(account)
167 }
168 fn into_raw_account(self) -> $ACCOUNT {
169 self.0
170 }
171 }
172
173}
174
175/// Account for the whole system
176///
177/// There will typically be only one of these for an entire Arti client or relay.
178///
179/// This is not really an [`Account`].
180/// We don't want anyone to make a Participant from this,
181/// because if that Participant were reclaimed, *everything* would be torn down.
182///
183/// We provide the type alias for consistency/readability at call sites.
184///
185/// See the [`memquota`](self) module documentation.
186pub type ToplevelAccount = Arc<MemoryQuotaTracker>;
187
188/// [`Account`] for a Tor Channel
189///
190/// Use via the [`SpecificAccount`] impl.
191/// See the [`memquota`](self) module documentation.
192#[derive(Deftly, Clone, Debug)]
193#[derive_deftly(SpecificAccount)]
194#[deftly(account_newtype(toplevel))]
195pub struct ChannelAccount(Account);
196
197/// [`Account`] for a Tor Circuit
198///
199/// Use via the [`SpecificAccount`] impl.
200/// See the [`memquota`](self) module documentation.
201#[derive(Deftly, Clone, Debug)]
202#[derive_deftly(SpecificAccount)]
203#[deftly(account_newtype(parent = "ChannelAccount"))]
204pub struct CircuitAccount(Account);
205
206/// [`Account`] for a Tor Stream
207///
208/// Use via the [`SpecificAccount`] impl.
209/// See the [`memquota`](self) module documentation.
210#[derive(Deftly, Clone, Debug)]
211#[derive_deftly(SpecificAccount)]
212#[deftly(account_newtype(parent = "CircuitAccount"))]
213pub struct StreamAccount(Account);