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

            
84
use derive_deftly::{define_derive_deftly, Deftly};
85
use std::sync::Arc;
86
use 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.
92
pub 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
675
    fn new_noop() -> Self {
119
675
        Self::from_raw_account(Account::new_noop())
120
675
    }
121
}
122

            
123
define_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
569
    ${define ACCOUNT { $crate::tor_memquota::Account }}
140
569

            
141
569
    ${defcond HAS_PARENT  tmeta(account_newtype(parent))}
142
569
    ${defcond IS_TOPLEVEL tmeta(account_newtype(toplevel))}
143
569

            
144
569
    ${define CONSTRUCTED_FROM {
145
569
        ${select1
146
569
          HAS_PARENT  { ${tmeta(account_newtype(parent)) as ty} }
147
569
          IS_TOPLEVEL { std::sync::Arc<$crate::tor_memquota::MemoryQuotaTracker> }
148
569
        }
149
569
    }}
150
569

            
151
569
    impl SpecificAccount for $ttype {
152
569
        type ConstructedFrom = $CONSTRUCTED_FROM;
153
569

            
154
1227
        fn new(src: &Self::ConstructedFrom) -> Result<Self, tor_memquota::Error> {
155
569
          ${select1
156
569
            HAS_PARENT  { $crate::memquota::SpecificAccount::as_raw_account(src).new_child() }
157
569
            IS_TOPLEVEL { src.new_account(None) }
158
569
          }
159
569
                .map(Self::from_raw_account)
160
569
        }
161
569

            
162
569
        fn as_raw_account(&self) -> &$ACCOUNT {
163
            &self.0
164
        }
165
1468
        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.
186
pub 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))]
195
pub 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"))]
204
pub 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"))]
213
pub struct StreamAccount(Account);