1
//! Memory quota tracker, core and low-level API
2
//!
3
//! # Example
4
//!
5
//! ```cfg(feature = "memquota")
6
//! use std::{collections::VecDeque, sync::{Arc, Mutex}};
7
//! use tor_rtcompat::{CoarseInstant, CoarseTimeProvider, PreferredRuntime};
8
//! use tor_memquota::{mtracker, MemoryQuotaTracker, MemoryReclaimedError, EnabledToken};
9
//! use void::{ResultVoidExt, Void};
10
//!
11
//! #[derive(Debug)]
12
//! struct TrackingQueue(Mutex<Result<Inner, MemoryReclaimedError>>);
13
//! #[derive(Debug)]
14
//! struct Inner {
15
//!     partn: mtracker::Participation,
16
//!     data: VecDeque<(Box<[u8]>, CoarseInstant)>,
17
//! }
18
//!
19
//! impl TrackingQueue {
20
//!     fn push(&self, now: CoarseInstant, bytes: Box<[u8]>) -> Result<(), MemoryReclaimedError> {
21
//!         let mut inner = self.0.lock().unwrap();
22
//!         let inner = inner.as_mut().map_err(|e| e.clone())?;
23
//!         inner.partn.claim(bytes.len())?;
24
//!         inner.data.push_back((bytes, now));
25
//!         Ok(())
26
//!     }
27
//! }
28
//!
29
//! impl mtracker::IsParticipant for TrackingQueue {
30
//!     fn get_oldest(&self, _: EnabledToken) -> Option<CoarseInstant> {
31
//!         let inner = self.0.lock().unwrap();
32
//!         Some(inner.as_ref().ok()?.data.front()?.1)
33
//!     }
34
//!     fn reclaim(self: Arc<Self>, _: EnabledToken) -> mtracker::ReclaimFuture {
35
//!         let mut inner = self.0.lock().unwrap();
36
//!         *inner = Err(MemoryReclaimedError::new());
37
//!         Box::pin(async { mtracker::Reclaimed::Collapsing })
38
//!     }
39
//! }
40
//!
41
//! let runtime = PreferredRuntime::create().unwrap();
42
//! let config  = tor_memquota::Config::builder().max(1024*1024*1024).build().unwrap();
43
#![cfg_attr(
44
    feature = "memquota",
45
    doc = "let trk = MemoryQuotaTracker::new(&runtime, config).unwrap();"
46
)]
47
#![cfg_attr(
48
    not(feature = "memquota"),
49
    doc = "let trk = MemoryQuotaTracker::new_noop();"
50
)]
51
//!
52
//! let account = trk.new_account(None).unwrap();
53
//!
54
//! let queue: Arc<TrackingQueue> = account.register_participant_with(
55
//!     runtime.now_coarse(),
56
//!     |partn| {
57
//!         Ok::<_, Void>((Arc::new(TrackingQueue(Mutex::new(Ok(Inner {
58
//!             partn,
59
//!             data: VecDeque::new(),
60
//!         })))), ()))
61
//!     },
62
//! ).unwrap().void_unwrap().0;
63
//!
64
//! queue.push(runtime.now_coarse(), Box::new([0; 24])).unwrap();
65
//! ```
66
//
67
// For key internal documentation about the data structure, see the doc comment for
68
// `struct State` (down in the middle of the file).
69

            
70
#![forbid(unsafe_code)] // if you remove this, enable (or write) miri tests (git grep miri)
71

            
72
use crate::internal_prelude::*;
73

            
74
use IfEnabled::*;
75

            
76
mod bookkeeping;
77
mod reclaim;
78
mod total_qty_notifier;
79

            
80
#[cfg(all(test, feature = "memquota", not(miri) /* coarsetime */))]
81
pub(crate) mod test;
82

            
83
use bookkeeping::{BookkeepableQty, ClaimedQty, ParticipQty, TotalQty};
84
use total_qty_notifier::TotalQtyNotifier;
85

            
86
/// Maximum amount we'll "cache" locally in a [`Participation`]
87
///
88
/// ie maximum value of `Participation.cache`.
89
//
90
// TODO is this a good amount? should it be configurable?
91
pub(crate) const MAX_CACHE: Qty = Qty(16384);
92

            
93
/// Target cache size when we seem to be claiming
94
const TARGET_CACHE_CLAIMING: Qty = Qty(MAX_CACHE.as_usize() * 3 / 4);
95
/// Target cache size when we seem to be releasing
96
#[allow(clippy::identity_op)] // consistency
97
const TARGET_CACHE_RELEASING: Qty = Qty(MAX_CACHE.as_usize() * 1 / 4);
98

            
99
//---------- public data types ----------
100

            
101
/// Memory data tracker
102
///
103
/// Instance of the memory quota system.
104
///
105
/// Usually found as `Arc<MemoryQuotaTracker>`.
106
#[derive(Debug)]
107
pub struct MemoryQuotaTracker {
108
    /// The actual tracker state etc.
109
    state: IfEnabled<Mutex<State>>,
110
}
111

            
112
/// Handle onto an Account
113
///
114
/// An `Account` is a handle.  All clones refer to the same underlying conceptual Account.
115
///
116
/// `Account`s are created using [`MemoryQuotaTracker::new_account`].
117
///
118
/// # Use in Arti
119
///
120
/// In Arti, we usually use a newtype around `Account`, rather than a bare `Account`.
121
/// See `tor_proto::memquota`.
122
32
#[derive(Educe)]
123
#[educe(Debug)]
124
pub struct Account(IfEnabled<AccountInner>);
125

            
126
/// Contents of an enabled [`Account`]
127
41768
#[derive(Educe)]
128
#[educe(Debug)]
129
pub struct AccountInner {
130
    /// The account ID
131
    aid: refcount::Ref<AId>,
132

            
133
    /// The underlying tracker
134
    #[educe(Debug(ignore))]
135
    tracker: Arc<MemoryQuotaTracker>,
136
}
137

            
138
/// Weak handle onto an Account
139
///
140
/// Like [`Account`], but doesn't keep the account alive.
141
/// Must be upgraded before use.
142
//
143
// Doesn't count for ARecord.account_clones
144
//
145
// We can't lift out Arc, so that the caller sees `Arc<Account>`,
146
// because an Account is Arc<MemoryQuotaTracker> plus AId,
147
// not Arc of something account-specific.
148
#[derive(Clone, Educe)]
149
#[educe(Debug)]
150
pub struct WeakAccount(IfEnabled<WeakAccountInner>);
151

            
152
/// Contents of an enabled [`WeakAccount`]
153
#[derive(Clone, Educe)]
154
#[educe(Debug)]
155
pub struct WeakAccountInner {
156
    /// The account ID
157
    aid: AId,
158

            
159
    /// The underlying tracker
160
    #[educe(Debug(ignore))]
161
    tracker: Weak<MemoryQuotaTracker>,
162
}
163

            
164
/// Handle onto a participant's participation in a tracker
165
///
166
/// `Participation` is a handle.  All clones are for use by the same conceptual Participant.
167
/// It doesn't keep the underlying Account alive.
168
///
169
/// `Participation`s are created by registering new participants,
170
/// for example using [`Account::register_participant`].
171
///
172
/// Variables of this type are often named `partn`.
173
#[derive(Debug)]
174
pub struct Participation(IfEnabled<ParticipationInner>);
175

            
176
/// Contents of an enabled [`Participation`]
177
#[derive(Debug)]
178
pub struct ParticipationInner {
179
    /// Participant id
180
    pid: refcount::Ref<PId>,
181

            
182
    /// Account id
183
    aid: AId,
184

            
185
    /// The underlying tracker
186
    tracker: Weak<MemoryQuotaTracker>,
187

            
188
    /// Quota we have preemptively claimed for use by this Account
189
    ///
190
    /// Has been added to `PRecord.used`,
191
    /// but not yet returned by `Participation::claim`.
192
    ///
193
    /// This cache field arranges that most of the time we don't have to hammer a
194
    /// single cache line.
195
    ///
196
    /// The value here is bounded by a configured limit.
197
    ///
198
    /// Invariants on memory accounting:
199
    ///
200
    ///  * `Participation.cache < configured limit`
201
    ///  * `PRecord.used = Participation.cache + Σ Participation::claim - Σ P'n::release`
202
    ///    except if `PRecord` has been deleted
203
    ///    (ie when we aren't tracking any more and think the Participant is `Collapsing`).
204
    ///  * `Σ PRecord.used = State.total_used`
205
    ///
206
    /// Enforcement of these invariants is partially assured by
207
    /// types in [`bookkeeping`].
208
    cache: ClaimedQty,
209
}
210

            
211
/// Participants provide an impl of the hooks in this trait
212
///
213
/// Trait implemented by client of the memtrack API.
214
///
215
/// # Panic handling, "unwind safety"
216
///
217
/// If these methods panic, the memory tracker will tear down its records of the
218
/// participant, preventing future allocations.
219
///
220
/// But, it's not guaranteed that these methods on `IsParticipant` won't be called again,
221
/// even if they have already panicked on a previous occasion.
222
/// Thus the implementations might see "broken invariants"
223
/// as discussed in the docs for `std::panic::UnwindSafe`.
224
///
225
/// Nevertheless we don't make `RefUnwindSafe` a supertrait of `IsParticipant`.
226
/// That would force the caller to mark *all* their methods unwind-safe,
227
/// which is unreasonable (and probably undesirable).
228
///
229
/// Variables which are `IsParticipant` are often named `particip`.
230
pub trait IsParticipant: Debug + Send + Sync + 'static {
231
    /// Return the age of the oldest data held by this Participant
232
    ///
233
    /// `None` means this Participant holds no data.
234
    ///
235
    /// # Performance and reentrancy
236
    ///
237
    /// This function runs with the `MemoryQuotaTracker`'s internal global lock held.
238
    /// Therefore:
239
    ///
240
    ///  * It must be fast.
241
    ///  * it *must not* call back into methods from [`tracker`](crate::mtracker).
242
    ///  * It *must not* even `Clone` or `Drop` a [`MemoryQuotaTracker`],
243
    ///    [`Account`], or [`Participation`].
244
    fn get_oldest(&self, _: EnabledToken) -> Option<CoarseInstant>;
245

            
246
    /// Start memory reclamation
247
    ///
248
    /// The Participant should start to free all of its memory,
249
    /// and then return `Reclaimed::Collapsing`.
250
    //
251
    // In the future:
252
    //
253
    // Should free *at least* all memory at least as old as discard_...
254
    //
255
    // v1 of the actual implementation might not have `discard_everything_as_old_as`
256
    // and `but_can_stop_discarding_...`,
257
    // and might therefore only support Reclaimed::Collapsing
258
    fn reclaim(
259
        self: Arc<Self>,
260
        _: EnabledToken,
261
        // Future:
262
        // discard_everything_as_old_as_this: RoughTime,
263
        // but_can_stop_discarding_after_freeing_this_much: Qty,
264
    ) -> ReclaimFuture;
265
}
266

            
267
/// Future returned by the [`IsParticipant::reclaim`] reclamation request
268
pub type ReclaimFuture = Pin<Box<dyn Future<Output = Reclaimed> + Send + Sync>>;
269

            
270
/// Outcome of [`IsParticipant::reclaim`]
271
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
272
#[non_exhaustive]
273
pub enum Reclaimed {
274
    /// Participant is responding to reclamation by collapsing completely.
275
    ///
276
    /// All memory will be freed and `release`'d soon (if it hasn't been already).
277
    /// `MemoryQuotaTracker` should forget the Participant and all memory it used, right away.
278
    ///
279
    /// Currently this is the only supported behaviour.
280
    Collapsing,
281
    // Future:
282
    // /// Participant has now reclaimed some memory as instructed
283
    // ///
284
    // /// If this is not sufficient, tracker must call reclaim() again.
285
    // /// (We may not want to implement Partial right away but the API
286
    // /// ought to support it so let's think about it now, even if we don't implement it.)
287
    // Partial,
288
}
289

            
290
//---------- principal data structure ----------
291

            
292
slotmap_careful::new_key_type! {
293
    /// Identifies an Account
294
    ///
295
    /// After an account is torn down, the `AId` becomes invalid
296
    /// and attempts to use it will give an error.
297
    ///
298
    /// The same `AId` won't be reused for a later Account.
299
    struct AId;
300

            
301
    /// Identifies a Participant within an Account
302
    ///
303
    /// Ie, PId is scoped within in the context of an account.
304
    ///
305
    /// As with `AId`, a `PId` is invalid after the
306
    /// participation is torn down, and is not reused.
307
    struct PId;
308
}
309

            
310
/// Memory tracker inner, including mutable state
311
///
312
/// # Module internal documentation
313
///
314
/// ## Data structure
315
///
316
///  * [`MemoryQuotaTracker`] contains mutex-protected `State`.
317
///  * The `State` contains a [`SlotMap`] of account records [`ARecord`].
318
///  * Each `ARecord` contains a `SlotMap` of participant records [`PRecord`].
319
///
320
/// The handles [`Account`], [`WeakAccount`], and [`Participation`],
321
/// each contain a reference (`Arc`/`Weak`) to the `MemoryQuotaTracker`,
322
/// and the necessary slotmap keys.
323
///
324
/// The `ARecord` and `PRecord` each contain a reference count,
325
/// which is used to clean up when all the handles are gone.
326
///
327
/// The slotmap keys which count for the reference count (ie, strong references)
328
/// are stored as [`refcount::Ref`],
329
/// which helps assure correct reference counting.
330
/// (Bare ids [`AId`] and [`PId`] are weak references.)
331
///
332
/// ## Data structure lookup
333
///
334
/// Given a reference to the tracker, and some ids, the macro `find_in_tracker!`
335
/// is used to obtain mutable references to the `ARecord` and (if applicable) `PRecord`.
336
///
337
/// ## Bookkeeping
338
///
339
/// We use separate types for quantities of memory in various "states",
340
/// rather than working with raw quantities.
341
///
342
/// The types, and the legitimate transactions, are in `bookkeeping`.
343
///
344
/// ## Reentrancy (esp. `Drop` and `Clone`)
345
///
346
/// When the handle structs are dropped or cloned, they must manipulate the refcount(s).
347
/// So they must take the lock.
348
/// Therefore, an `Account` and `Participation` may not be dropped with the lock held!
349
///
350
/// Internally, this is actually fairly straightforward:
351
/// we take handles by reference, and constructors only make them at the last moment on return,
352
/// so our internal code here, in this module, doesn't have owned handles.
353
///
354
/// We also need to worry about reentrantly reentering the tracker code, from user code.
355
/// The user supplies a `dyn IsParticipant`.
356
/// The principal methods are from [`IsParticipant`],
357
/// for which we handle reentrancy in the docs.
358
/// But we also implicitly invoke its `Drop` impl, which might in turn drop stuff of ours,
359
/// such as [`Account`]s and [`Participation`]s, whose `Drop` impls need to take our lock.
360
/// To make sure this isn't done reentrantly, we have a special newtype around it,
361
/// and defer some of our drops during reclaim.
362
/// That's in `drop_reentrancy` and `tracker::reclaim::deferred_drop`.
363
///
364
/// The `Debug` impl isn't of concern, since we don't call it ourselves.
365
/// And we don't rely on it being `Clone`, since it's in an `Arc`.
366
///
367
/// ## Drop bombs
368
///
369
/// With `#[cfg(test)]`, several of our types have "drop bombs":
370
/// they cause a panic if dropped inappropriately.
371
/// This is intended to detect bad code paths during testing.
372
#[derive(Debug, Deref, DerefMut)]
373
struct State {
374
    /// Global parts of state
375
    ///
376
    /// Broken out to allow passing both
377
    /// `&mut Global` and `&mut ARecord`/`&mut PRecord`
378
    /// to some function(s).
379
    #[deref]
380
    #[deref_mut]
381
    global: Global,
382

            
383
    /// Accounts
384
    accounts: SlotMap<AId, ARecord>,
385
}
386

            
387
/// Global parts of `State`
388
#[derive(Debug)]
389
struct Global {
390
    /// Total memory used
391
    ///
392
    /// Wrapper type for ensuring we wake up the reclamation task
393
    total_used: TotalQtyNotifier,
394

            
395
    /// Configuration
396
    config: ConfigInner,
397

            
398
    /// Make this type uninhabited if memory tracking is compiled out
399
    #[allow(dead_code)]
400
    enabled: EnabledToken,
401
}
402

            
403
/// Account record, within `State.accounts`
404
#[derive(Debug)]
405
#[must_use = "don't just drop, call auto_release"]
406
struct ARecord {
407
    /// Number of clones of `Account`; to know when to tear down the account
408
    refcount: refcount::Count<AId>,
409

            
410
    /// Child accounts
411
    children: Vec<AId>,
412

            
413
    /// Participants linked to this Account
414
    ps: SlotMap<PId, PRecord>,
415

            
416
    /// Make this type uninhbaited if memory tracking is compiled out
417
    #[allow(dead_code)]
418
    enabled: EnabledToken,
419
}
420

            
421
/// Participant record, within `ARecord.ps`
422
#[derive(Debug)]
423
#[must_use = "don't just drop, call auto_release"]
424
struct PRecord {
425
    /// Number of clones of `Participation`; to know when to tear down the participant
426
    refcount: refcount::Count<PId>,
427

            
428
    /// Memory usage of this participant
429
    ///
430
    /// Not 100% accurate, can lag, and be (boundedly) an overestimate
431
    used: ParticipQty,
432

            
433
    /// The hooks provided by the Participant
434
    particip: drop_reentrancy::ProtectedWeak<dyn IsParticipant>,
435

            
436
    /// Make this type uninhabited if memory tracking is compiled out
437
    #[allow(dead_code)]
438
    enabled: EnabledToken,
439
}
440

            
441
//#################### IMPLEMENTATION ####################
442

            
443
/// Given a `&Weak<MemoryQuotaTracker>`, find an account and maybe participant
444
///
445
/// ### Usage templates
446
///
447
/// ```rust,ignore
448
/// find_in_tracker! {
449
///     enabled;
450
///     weak_tracker => + tracker, state;
451
///     aid => arecord;
452
///   [ pid => precord; ]
453
///   [ ?Error | ?None ]
454
/// };
455
///
456
/// find_in_tracker! {
457
///     enabled;
458
///     strong_tracker => state;
459
///     .. // as above
460
/// };
461
/// ```
462
///
463
/// ### Input expressions (value arguments to the macro0
464
///
465
///  * `weak_tracker: &Weak<MemoryQuotaTracker>` (or equivalent)
466
///  * `strong_tracker: &MemoryQuotaTracker` (or equivalent)
467
///  * `enabled: EnabledToken` (or equivalent)
468
///  * `aid: AId`
469
///  * `pid: PId`
470
///
471
/// ### Generated bindings (identifier arguments to the macro)
472
///
473
///  * `tracker: Arc<MemoryQuotaTracker>`
474
///  * `state: &mut State` (borrowed from a `MutexGuard<State>` borrowed from `tracker`)
475
///  * `arecord: &mut ARecord` (mut borrowed from `state.accounts`)
476
///  * `precord: &mut PRecord` (mut borrowed from `arecord.ps`)
477
///
478
/// There is no access to the `MutexGuard` itself.
479
/// For control of the mutex release point, place `find_in_tracker!` in an enclosing block.
480
///
481
/// ### Error handling
482
///
483
/// If the tracker, account, or participant, can't be found,
484
/// the macro returns early from the enclosing scope (using `?`).
485
///
486
/// If `Error` is specified, applies `?` to `Err(Error::...)`.
487
/// If `None` is specified, just returns `None` (by applying `?` to None`).
488
//
489
// This has to be a macro because it makes a self-referential set of bindings.
490
// Input syntax is a bit janky because macro_rules is so bad.
491
// For an internal macro with ~9 call sites it's not worth making a big parsing contraption.
492
macro_rules! find_in_tracker { {
493
    // This `+` is needed because otherwise it's LL1-ambiguous and macro_rules can't cope
494
    $enabled:expr;
495
    $tracker_input:expr => $( + $tracker:ident, )? $state:ident;
496
    $aid:expr => $arecord:ident;
497
 $( $pid:expr => $precord:ident; )?
498
    // Either `Error` or None, to be passed to `find_in_tracker_eh!($eh ...: ...)`
499
    // (We need this to be an un-repeated un-optional binding, because
500
    // it is used within some other $( ... )?, and macro_rules gets confused.)
501
    ? $eh:tt
502
} => {
503
    let tracker = &$tracker_input;
504
  $(
505
    let $tracker: Arc<MemoryQuotaTracker> = find_in_tracker_eh!(
506
        $eh Error::TrackerShutdown;
507
        tracker.upgrade()
508
    );
509
    let tracker = &$tracker;
510
  )?
511
    let _: &EnabledToken = &$enabled;
512
    let state = find_in_tracker_eh!(
513
        $eh Error::Bug(internal!("noop MemoryQuotaTracker found via enabled datastructure"));
514
        tracker.state.as_enabled()
515
    );
516
    let mut state: MutexGuard<State> = find_in_tracker_eh!(
517
        $eh Error::TrackerCorrupted;
518
        state.lock().ok()
519
    );
520
    let $state: &mut State = &mut *state;
521
    let aid: AId = $aid;
522
    let $arecord: &mut ARecord = find_in_tracker_eh!(
523
        $eh Error::AccountClosed;
524
        $state.accounts.get_mut(aid)
525
    );
526
  $(
527
    let pid: PId = $pid;
528
    let $precord: &mut PRecord = find_in_tracker_eh!(
529
        $eh Error::ParticipantShutdown;
530
        $arecord.ps.get_mut(pid)
531
    );
532
  )?
533
} }
534
/// Error handling helper for `find_in_tracker`
535
macro_rules! find_in_tracker_eh {
536
    { None $variant:expr; $result:expr } => { $result? };
537
    { Error $variant:expr; $result:expr } => { $result.ok_or_else(|| $variant)? };
538
}
539

            
540
//========== impls on public types, including public methods and trait impls ==========
541

            
542
//---------- MemoryQuotaTracker ----------
543

            
544
impl MemoryQuotaTracker {
545
    /// Set up a new `MemoryDataTracker`
546
52
    pub fn new<R: Spawn>(runtime: &R, config: Config) -> Result<Arc<Self>, StartupError> {
547
52
        let Enabled(config, enabled) = config.0 else {
548
8
            return Ok(MemoryQuotaTracker::new_noop());
549
        };
550

            
551
44
        let (reclaim_tx, reclaim_rx) =
552
44
            mpsc_channel_no_memquota(0 /* plus num_senders, ie 1 */);
553
44
        let total_used = TotalQtyNotifier::new_zero(reclaim_tx);
554
44
        let ConfigInner { max, low_water } = config; // for logging
555
44

            
556
44
        let global = Global {
557
44
            total_used,
558
44
            config,
559
44
            enabled,
560
44
        };
561
44
        let accounts = SlotMap::default();
562
44
        let state = Enabled(Mutex::new(State { global, accounts }), enabled);
563
44
        let tracker = Arc::new(MemoryQuotaTracker { state });
564
44

            
565
44
        // We don't provide a separate `launch_background_tasks`, because this task doesn't
566
44
        // wake up periodically, or, indeed, do anything until the tracker is used.
567
44

            
568
44
        let for_task = Arc::downgrade(&tracker);
569
44
        runtime.spawn(reclaim::task(for_task, reclaim_rx, enabled))?;
570

            
571
44
        info!(%max, %low_water, "memory quota tracking initialised");
572

            
573
44
        Ok(tracker)
574
52
    }
575

            
576
    /// Reconfigure
577
138
    pub fn reconfigure(
578
138
        &self,
579
138
        new_config: Config,
580
138
        how: tor_config::Reconfigure,
581
138
    ) -> Result<(), ReconfigureError> {
582
        use tor_config::Reconfigure;
583

            
584
138
        let state = self.lock().map_err(into_internal!(
585
138
            "cannot reconfigure corrupted memquota tracker"
586
138
        ))?;
587

            
588
138
        let (state, new_config) = match (state, new_config.0) {
589
138
            (Noop, Noop) => return Ok(()),
590
            (Noop, Enabled(..)) => return how.cannot_change(
591
                // TODO #1577 (3) this isn't the `field` wanted by `cannot_change`
592
 "tor-memquota max (`system.memory.max`) cannot be set: cannot enable memory quota tracking, when disabled at program start"
593
            ),
594
            (Enabled(state, _enabled), new_config) => {
595
                let new_config = new_config.into_enabled().unwrap_or(
596
                    // If the new configuration is "Noop", set the limit values to MAX
597
                    // so we will never think we want to reclaim.
598
                    // We don't replace ourselves with a Noop or something,
599
                    // in case the user wants to re-enable tracking.
600
                    ConfigInner {
601
                        max: Qty::MAX,
602
                        low_water: Qty::MAX,
603
                    },
604
                );
605

            
606
                (state, new_config)
607
            },
608
        };
609

            
610
        // Bind state mutably only if we're supposed to actually be modifying anything
611
        let mut state = match how {
612
            Reconfigure::CheckAllOrNothing => return Ok(()),
613
            Reconfigure::AllOrNothing | Reconfigure::WarnOnFailures => state,
614
            _ => Err(internal!("Reconfigure variant unknown! {how:?}"))?, // TODO #1577 (1)
615
        };
616

            
617
        let global = &mut state.global;
618
        global.config = new_config;
619

            
620
        // If the new limit is lower, we might need to start reclaiming:
621
        global.total_used.maybe_wakeup(&global.config);
622

            
623
        // If the new low_water is higher, we might need to *stop* reclaiming.
624
        // We don't have a way to abort an ongoing reclaim request,
625
        // but the usage vs low_water will be rechecked before we reclaim
626
        // from another Participant, which will be sufficient.
627

            
628
        Ok(())
629
138
    }
630

            
631
    /// Returns an estimate of the total memory use
632
    ///
633
    /// The returned value is:
634
    ///
635
    ///  * [Approximate.](../index.html#is-approximate)
636
    ///  * A snapshot as of the current moment (and there is no way to await changes)
637
    ///  * Always `usize::MAX` for a no-op tracker
638
12
    pub fn used_current_approx(&self) -> Result<usize, TrackerCorrupted> {
639
12
        let Enabled(state, _enabled) = self.lock()? else {
640
            return Ok(usize::MAX);
641
        };
642
12
        Ok(*state.total_used.as_raw())
643
12
    }
644

            
645
    /// Make a new `Account`
646
    ///
647
    /// To actually record memory usage, a Participant must be added.
648
    ///
649
    /// At most call sites, take an `Account` rather than a `MemoryQuotaTracker`,
650
    /// and use [`Account::new_child()`].
651
    /// That improves the ability to manage the hierarchy of Participants.
652
    //
653
    // Right now, parent can't be changed after construction of an Account,
654
    // so circular accounts are impossible.
655
    // But, we might choose to support that in the future.
656
    // Circular parent relationships might need just a little care
657
    // in the reclamation loop (to avoid infinitely looping),
658
    // but aren't inherently unsupportable.
659
    #[allow(clippy::redundant_closure_call)] // We have IEFEs for good reasons
660
1191
    pub fn new_account(self: &Arc<Self>, parent: Option<&Account>) -> crate::Result<Account> {
661
1191
        let Enabled(mut state, enabled) = self.lock()? else {
662
1035
            return Ok(Account(Noop));
663
        };
664

            
665
156
        let parent_aid_good = parent
666
160
            .map(|parent| {
667
                // Find and check the requested parent's Accountid
668

            
669
8
                let Enabled(parent, _enabled) = &parent.0 else {
670
                    return Err(
671
                        internal!("used no-op Account as parent for enabled new_account").into(),
672
                    );
673
                };
674

            
675
8
                let parent_aid = *parent.aid;
676
8
                let parent_arecord = state
677
8
                    .accounts
678
8
                    .get_mut(parent_aid)
679
8
                    .ok_or(Error::AccountClosed)?;
680

            
681
                // Can we insert the new child without reallocating?
682
8
                if !parent_arecord.children.spare_capacity_mut().is_empty() {
683
                    return Ok(parent_aid);
684
8
                }
685
8

            
686
8
                // No.  Well, let's do some garbage collection.
687
8
                // (Otherwise .children might grow without bound as accounts come and go)
688
8
                //
689
8
                // We would like to scan the accounts array while mutating this account.
690
8
                // Instead, steal the children array temporarily and put the filtered one back.
691
8
                // Must be infallible!
692
8
                //
693
8
                // The next line can't be in the closure (confuses borrowck)
694
8
                let mut parent_children = mem::take(&mut parent_arecord.children);
695
8
                (|| {
696
8
                    parent_children.retain(|child_aid| state.accounts.contains_key(*child_aid));
697
8

            
698
8
                    // Put the filtered list back, so sanity is restored.
699
8
                    state
700
8
                        .accounts
701
8
                        .get_mut(parent_aid)
702
8
                        .expect("parent vanished!")
703
8
                        .children = parent_children;
704
8
                })();
705
8

            
706
8
                Ok::<_, Error>(parent_aid)
707
160
            })
708
156
            .transpose()?;
709

            
710
        // We have resolved the parent AId and prepared to add the new account to its list of
711
        // children.  We still hold the lock, so nothing can have changed.
712

            
713
        // commitment - infallible IEFE assures that so we don't do half of it
714
234
        Ok((|| {
715
156
            let aid = refcount::slotmap_insert(&mut state.accounts, |refcount| ARecord {
716
156
                refcount,
717
156
                children: vec![],
718
156
                ps: SlotMap::default(),
719
156
                enabled,
720
156
            });
721

            
722
156
            if let Some(parent_aid_good) = parent_aid_good {
723
8
                state
724
8
                    .accounts
725
8
                    .get_mut(parent_aid_good)
726
8
                    .expect("parent vanished!")
727
8
                    .children
728
8
                    .push(*aid);
729
148
            }
730

            
731
156
            let tracker = self.clone();
732
156
            let inner = AccountInner { aid, tracker };
733
156
            Account(Enabled(inner, enabled)) // don't make this fallible, see above.
734
156
        })())
735
1191
    }
736

            
737
    /// Obtain a new `MemoryQuotaTracker` that doesn't track anything and never reclaims
738
2070
    pub fn new_noop() -> Arc<MemoryQuotaTracker> {
739
2070
        Arc::new(MemoryQuotaTracker { state: Noop })
740
2070
    }
741

            
742
    /// Obtain the lock on the state
743
41633
    fn lock(&self) -> Result<IfEnabled<MutexGuard<State>>, TrackerCorrupted> {
744
41633
        let Enabled(state, enabled) = &self.state else {
745
1173
            return Ok(Noop);
746
        };
747
40460
        Ok(Enabled(state.lock()?, *enabled))
748
41633
    }
749
}
750

            
751
//---------- Account ----------
752

            
753
impl Account {
754
    /// Register a new Participant
755
    ///
756
    /// Returns the [`Participation`], which can be used to record memory allocations.
757
    ///
758
    /// Often, your implementation of [`IsParticipant`] wants to contain the [`Participation`].
759
    /// If so, use [`register_participant_with`](Account::register_participant_with) instead.
760
172
    pub fn register_participant(
761
172
        &self,
762
172
        particip: Weak<dyn IsParticipant>,
763
172
    ) -> Result<Participation, Error> {
764
172
        let Enabled(self_, enabled) = &self.0 else {
765
            return Ok(Participation(Noop));
766
        };
767

            
768
172
        let aid = *self_.aid;
769
172
        find_in_tracker! {
770
            enabled;
771
            self_.tracker => state;
772
172
            aid => arecord;
773
            ?Error
774
        }
775

            
776
252
        let (pid, cache) = refcount::slotmap_try_insert(&mut arecord.ps, |refcount| {
777
168
            let mut precord = PRecord {
778
168
                refcount,
779
168
                used: ParticipQty::ZERO,
780
168
                particip: drop_reentrancy::ProtectedWeak::new(particip),
781
168
                enabled: *enabled,
782
168
            };
783
168
            let cache =
784
168
                state
785
168
                    .global
786
168
                    .total_used
787
168
                    .claim(&mut precord, MAX_CACHE, &state.global.config)?;
788
168
            Ok::<_, Error>((precord, cache))
789
252
        })?;
790

            
791
168
        let tracker = Arc::downgrade(&self_.tracker);
792
168
        let inner = ParticipationInner {
793
168
            tracker,
794
168
            pid,
795
168
            aid,
796
168
            cache,
797
168
        };
798
168
        Ok(Participation(Enabled(inner, *enabled)))
799
172
    }
800

            
801
    /// Set the callbacks for a Participant (identified by its weak ids)
802
164
    fn set_participant_callbacks(
803
164
        &self,
804
164
        aid: AId,
805
164
        pid: PId,
806
164
        particip: drop_reentrancy::ProtectedWeak<dyn IsParticipant>,
807
164
    ) -> Result<(), Error> {
808
164
        let Enabled(self_, enabled) = &self.0 else {
809
            return Ok(());
810
        };
811
164
        find_in_tracker! {
812
            enabled;
813
            self_.tracker => state;
814
164
            aid => arecord;
815
164
            pid => precord;
816
            ?Error
817
        }
818
164
        precord.particip = particip;
819
164
        Ok(())
820
164
    }
821

            
822
    /// Register a new Participant using a constructor
823
    ///
824
    /// Passes `constructor` a [`Participation`] for the nascent Participant.
825
    /// Returns the `P: IsParticipant` provided by the constructor.
826
    ///
827
    /// For use when your `impl `[`IsParticipant`] wants to own the `Participation`.
828
    ///
829
    /// # Re-entrancy guarantees
830
    ///
831
    /// The `Participation` *may* be used by `constructor` for claiming memory use,
832
    /// even during construction.
833
    /// `constructor` may also clone the `Participation`, etc.
834
    ///
835
    /// Reclamation callbacks (via the `P as IsParticipant` impl) cannot occur
836
    /// until `constructor` returns.
837
    ///
838
    /// # Error handling
839
    ///
840
    /// Failures can occur before `constructor` is called,
841
    /// or be detected afterwards.
842
    /// If a failure is detected after `constructor` returns,
843
    /// the `Arc<P>` from `constructor` will be dropped
844
    /// (resulting in `P` being dropped, unless `constructor` kept another clone of it).
845
    ///
846
    /// `constructor` may also fail (throwing a different error type, `E`),
847
    /// in which case `register_participant_with` returns `Ok(Err(E))`.
848
    ///
849
    /// On successful setup of the Participant, returns `Ok(Ok(Arc<P>))`.
850
2057
    pub fn register_participant_with<P: IsParticipant, X, E>(
851
2057
        &self,
852
2057
        now: CoarseInstant,
853
2057
        constructor: impl FnOnce(Participation) -> Result<(Arc<P>, X), E>,
854
2057
    ) -> Result<Result<(Arc<P>, X), E>, Error> {
855
2057
        let Enabled(_self, _enabled) = &self.0 else {
856
1893
            return Ok(constructor(Participation(Noop)));
857
        };
858

            
859
        use std::sync::atomic::{AtomicBool, Ordering};
860

            
861
        /// Temporary participant, which stands in during construction
862
        #[derive(Debug)]
863
        struct TemporaryParticipant {
864
            /// The age, which is right now.  We hope this is all fast!
865
            now: CoarseInstant,
866
            /// Did someone call reclaim() ?
867
            collapsing: AtomicBool,
868
        }
869

            
870
        impl IsParticipant for TemporaryParticipant {
871
            fn get_oldest(&self, _: EnabledToken) -> Option<CoarseInstant> {
872
                Some(self.now)
873
            }
874
            fn reclaim(self: Arc<Self>, _: EnabledToken) -> ReclaimFuture {
875
                self.collapsing.store(true, Ordering::Release);
876
                Box::pin(async { Reclaimed::Collapsing })
877
            }
878
        }
879

            
880
164
        let temp_particip = Arc::new(TemporaryParticipant {
881
164
            now,
882
164
            collapsing: false.into(),
883
164
        });
884

            
885
164
        let partn = self.register_participant(Arc::downgrade(&temp_particip) as _)?;
886
164
        let partn_ = partn
887
164
            .0
888
164
            .as_enabled()
889
164
            .ok_or_else(|| internal!("Enabled Account gave Noop Participant"))?;
890
164
        let aid = partn_.aid;
891
164
        let pid_weak = *partn_.pid;
892

            
893
        // We don't hold the state lock here.  register_participant took it and released it.
894
        // This is important, because the constructor might call claim!
895
        // (And, also, we don't want the constructor panicking to poison the whole tracker.)
896
        // But it means there can be quite a lot of concurrent excitement,
897
        // including, theoretically, a possible reclaim.
898
164
        let (particip, xdata) = match constructor(partn) {
899
164
            Ok(y) => y,
900
            Err(e) => return Ok(Err(e)),
901
        };
902
164
        let particip = drop_reentrancy::ProtectedArc::new(particip);
903
164

            
904
164
        // IEFE prevents use from accidentally dropping `particip` until we mean to
905
164
        let r = (|| {
906
164
            let weak = {
907
164
                let weak = particip.downgrade();
908
164

            
909
164
                // Trait cast, from Weak<P> to Weak<dyn IsParticipant>.
910
164
                // We can only do this for a primitive, so we must unprotect
911
164
                // the Weak, converr it, and protect it again.
912
164
                drop_reentrancy::ProtectedWeak::new(weak.unprotect() as _)
913
164
            };
914
164
            self.set_participant_callbacks(aid, pid_weak, weak)?;
915

            
916
164
            if temp_particip.collapsing.load(Ordering::Acquire) {
917
                return Err(Error::ParticipantShutdown);
918
164
            }
919
164
            Ok(())
920
164
        })();
921
164

            
922
164
        let particip = particip.promise_dropping_is_ok();
923
164
        r?;
924
164
        Ok(Ok((particip, xdata)))
925
2057
    }
926

            
927
    /// Obtain a new `Account` which is a child of this one
928
    ///
929
    /// Equivalent to
930
    /// [`MemoryQuotaTracker.new_account`](MemoryQuotaTracker::new_account)`(Some(..))`
931
3036
    pub fn new_child(&self) -> crate::Result<Self> {
932
3036
        let Enabled(self_, _enabled) = &self.0 else {
933
3036
            return Ok(Account::new_noop());
934
        };
935
        self_.tracker.new_account(Some(self))
936
3036
    }
937

            
938
    /// Obtains a handle for the `MemoryQuotaTracker`
939
    pub fn tracker(&self) -> Arc<MemoryQuotaTracker> {
940
        let Enabled(self_, _enabled) = &self.0 else {
941
            return MemoryQuotaTracker::new_noop();
942
        };
943
        self_.tracker.clone()
944
    }
945

            
946
    /// Downgrade to a weak handle for the same Account
947
8
    pub fn downgrade(&self) -> WeakAccount {
948
8
        let Enabled(self_, enabled) = &self.0 else {
949
            return WeakAccount(Noop);
950
        };
951
8
        let inner = WeakAccountInner {
952
8
            aid: *self_.aid,
953
8
            tracker: Arc::downgrade(&self_.tracker),
954
8
        };
955
8
        WeakAccount(Enabled(inner, *enabled))
956
8
    }
957

            
958
    /// Obtain a new `Account` that does nothing and has no associated tracker
959
    ///
960
    /// All methods on this succeed, but they don't do anything.
961
61065
    pub fn new_noop() -> Self {
962
61065
        Account(IfEnabled::Noop)
963
61065
    }
964
}
965

            
966
impl Clone for Account {
967
10888
    fn clone(&self) -> Account {
968
10888
        let Enabled(self_, enabled) = &self.0 else {
969
10764
            return Account(Noop);
970
        };
971
124
        let tracker = self_.tracker.clone();
972
186
        let aid = (|| {
973
124
            let aid = *self_.aid;
974
124
            find_in_tracker! {
975
                enabled;
976
                tracker => state;
977
124
                aid => arecord;
978
                ?None
979
            }
980
120
            let aid = refcount::Ref::new(aid, &mut arecord.refcount).ok()?;
981
            // commitment point
982
120
            Some(aid)
983
124
        })()
984
126
        .unwrap_or_else(|| {
985
4
            // Either the account has been closed, or our refcount overflowed.
986
4
            // Return a busted `Account`, which always fails when we try to use it.
987
4
            //
988
4
            // If the problem was a refcount overflow, we're technically violating the
989
4
            // documented behaviour, since the returned `Account` isn't equivalent
990
4
            // to the original.  We could instead choose to tear down the Account;
991
4
            // that would be legal; but it's a lot of code to marginally change the
992
4
            // behaviour for a very unlikely situation.
993
4
            refcount::Ref::null()
994
126
        });
995
124
        let inner = AccountInner { aid, tracker };
996
124
        Account(Enabled(inner, *enabled))
997
10888
    }
998
}
999

            
impl Drop for Account {
73144
    fn drop(&mut self) {
73144
        let Enabled(self_, enabled) = &mut self.0 else {
72864
            return;
        };
280
        (|| {
280
            find_in_tracker! {
                enabled;
                self_.tracker => state;
280
                *self_.aid => arecord;
                ?None
            }
100
            if let Some(refcount::Garbage(mut removed)) =
220
                slotmap_dec_ref!(&mut state.accounts, self_.aid.take(), &mut arecord.refcount)
100
            {
100
                // This account is gone.  Automatically release everything.
100
                removed.auto_release(state);
120
            }
220
            Some(())
280
        })()
310
        .unwrap_or_else(|| {
60
            // Account has been torn down.  Dispose of the strong ref.
60
            // (This has no effect except in cfg(test), when it defuses the drop bombs)
60
            self_.aid.take().dispose_container_destroyed();
310
        });
73144
    }
}
//---------- WeakAccount ----------
impl WeakAccount {
    /// Upgrade to an `Account`, if the account still exists
    ///
    /// No-op `WeakAccounts` can always be upgraded.
16
    pub fn upgrade(&self) -> crate::Result<Account> {
16
        let Enabled(self_, enabled) = &self.0 else {
            return Ok(Account(Noop));
        };
16
        let aid = self_.aid;
        // (we must use a block, and can't use find_in_tracker's upgrade, because borrowck)
16
        let tracker = self_.tracker.upgrade().ok_or(Error::TrackerShutdown)?;
        let aid = {
8
            find_in_tracker! {
                enabled;
                tracker => state;
8
                aid => arecord;
                ?Error
            }
            refcount::Ref::new(aid, &mut arecord.refcount)?
            // commitment point
        };
        let inner = AccountInner { aid, tracker };
        Ok(Account(Enabled(inner, *enabled)))
16
    }
    /// Obtains a handle onto the `MemoryQuotaTracker`
    ///
    /// The returned handle is itself weak, and needs to be upgraded before use.
    ///
    /// If the `Account` was made a no-op `MemoryQuotaTracker`
    /// (ie, one from [`MemoryQuotaTracker::new_noop`])
    /// the returned value is always `Weak`.
    pub fn tracker(&self) -> Weak<MemoryQuotaTracker> {
        let Enabled(self_, _enabled) = &self.0 else {
            return Weak::default();
        };
        self_.tracker.clone()
    }
    /// Creates a new dangling, dummy, `WeakAccount`
    ///
    /// This can be used as a standin where a value of type `WeakAccount` is needed.
    /// The returned value cannot be upgraded to an `Account`,
    /// so cannot be used to claim memory or find a `MemoryQuotaTracker`.
    ///
    /// (If memory quota tracking is disabled at compile time,
    /// the returned value *can* be upgraded, to a no-op `Account`.)
    pub fn new_dangling() -> Self {
        let Some(enabled) = EnabledToken::new_if_compiled_in() else {
            return WeakAccount(Noop);
        };
        let inner = WeakAccountInner {
            aid: AId::default(),
            tracker: Weak::default(),
        };
        WeakAccount(Enabled(inner, enabled))
    }
}
//---------- Participation ----------
impl Participation {
    /// Record that some memory has been (or will be) allocated
231400
    pub fn claim(&mut self, want: usize) -> crate::Result<()> {
231400
        self.claim_qty(Qty(want))
231400
    }
    /// Record that some memory has been (or will be) allocated (using `Qty`)
231400
    pub(crate) fn claim_qty(&mut self, want: Qty) -> crate::Result<()> {
231400
        self.claim_qty_inner(want)
231416
            .inspect_err(|e| trace_report!(e, "claim {}", want))
231400
    }
    /// Record that some memory has been (or will be) allocated - core implementation
    ///
    /// Caller must handles trace logging.
231400
    fn claim_qty_inner(&mut self, want: Qty) -> crate::Result<()> {
231400
        let Enabled(self_, enabled) = &mut self.0 else {
210864
            return Ok(());
        };
        // In debug builds, check that the Account is still live, to detect lifetime trouble
        // (we repeat this later, which is OK in a debug build)
        #[cfg(debug_assertions)]
        {
20536
            find_in_tracker! {
                enabled;
                self_.tracker => + tracker, state;
20528
                self_.aid => _arecord;
20512
                *self_.pid => _precord;
                ?Error
            };
        }
20504
        if let Some(got) = self_.cache.split_off(want) {
6128
            return got.claim_return_to_participant();
14376
        }
14376

            
14376
        find_in_tracker! {
            enabled;
            self_.tracker => + tracker, state;
14376
            self_.aid => arecord;
14376
            *self_.pid => precord;
            ?Error
        };
30676
        let mut claim = |want| -> Result<ClaimedQty, _> {
23488
            state
23488
                .global
23488
                .total_used
23488
                .claim(precord, want, &state.global.config)
23488
        };
14376
        let got = claim(want)?;
14376
        if want <= TARGET_CACHE_CLAIMING {
            // While we're here, fill the cache to TARGET_CACHE_CLAIMING.
            // Cannot underflow: cache < want (since we failed at `got` earlier
            // and we've just checked want <= TARGET_CACHE_CLAIMING.
9112
            let want_more_cache = TARGET_CACHE_CLAIMING
9112
                .checked_sub(*self_.cache.as_raw())
9112
                .expect("but cache < want");
9112
            let want_more_cache = Qty(want_more_cache);
9112
            if let Ok(add_cache) = claim(want_more_cache) {
9112
                // On error, just don't do this; presumably the error will show up later
9112
                // (we mustn't early exit here, because we've got the claim in our hand).
9112
                self_.cache.merge_into(add_cache);
9112
            }
5264
        }
14376
        got.claim_return_to_participant()
231400
    }
    /// Record that some memory has been (or will be) freed by a participant
220786
    pub fn release(&mut self, have: usize) // infallible
220786
    {
220786
        self.release_qty(Qty(have));
220786
    }
    /// Record that some memory has been (or will be) freed by a participant (using `Qty`)
220786
    pub(crate) fn release_qty(&mut self, have: Qty) // infallible
220786
    {
220786
        let Enabled(self_, enabled) = &mut self.0 else {
200790
            return;
        };
19996
        let have = ClaimedQty::release_got_from_participant(have);
19996
        self_.cache.merge_into(have);
19996
        if self_.cache > MAX_CACHE {
6520
            match (|| {
6520
                find_in_tracker! {
                    enabled;
                    self_.tracker => + tracker, state;
6520
                    self_.aid => arecord;
6516
                    *self_.pid => precord;
                    ?None
                }
6516
                let return_from_cache = self_
6516
                    .cache
6516
                    .as_raw()
6516
                    .checked_sub(*TARGET_CACHE_RELEASING)
6516
                    .expect("TARGET_CACHE_RELEASING > MAX_CACHE ?!");
6516
                let return_from_cache = Qty(return_from_cache);
6516
                let from_cache = self_
6516
                    .cache
6516
                    .split_off(return_from_cache)
6516
                    .expect("impossible");
6516
                state.global.total_used.release(precord, from_cache);
6516
                Some(())
6520
            })() {
6516
                Some(()) => {} // we've given our cache back to the tracker
4
                None => {
4
                    // account (or whole tracker!) is gone
4
                    // throw away the cache so that we don't take this path again for a bit
4
                    self_.cache.take().dispose_participant_destroyed();
4
                }
            }
13476
        }
220786
    }
    /// Obtain a handle onto the account
    ///
    /// The returned handle is weak, and needs to be upgraded before use,
    /// since a [`Participation`] doesn't keep its Account alive.
    ///
    /// The returned `WeakAccount` is equivalent to
    /// all the other account handles for the same account.
8
    pub fn account(&self) -> WeakAccount {
8
        let Enabled(self_, enabled) = &self.0 else {
            return WeakAccount(Noop);
        };
8
        let inner = WeakAccountInner {
8
            aid: self_.aid,
8
            tracker: self_.tracker.clone(),
8
        };
8
        WeakAccount(Enabled(inner, *enabled))
8
    }
    /// Destroy this participant
    ///
    /// Treat as freed all the memory allocated via this `Participation` and its clones.
    /// After this, other clones of this `Participation` are no longer usable:
    /// attempts to do so will give errors.
    /// (although they can still be used to get at the `Account`, if it still exists).
    ///
    /// The actual memory should be freed promptly.
    ///
    /// (It is not necessary to call this function in order to get the memory tracker
    /// to free its handle onto the `IsParticipant`,
    /// because the memory quota system holds only a [`Weak`] reference.)
57570
    pub fn destroy_participant(mut self) {
57570
        let Enabled(self_, enabled) = &mut self.0 else {
57546
            return;
        };
24
        (|| {
24
            find_in_tracker! {
                enabled;
                self_.tracker => + tracker, state;
24
                self_.aid => arecord;
                ?None
            };
24
            if let Some(mut removed) =
24
                refcount::slotmap_remove_early(&mut arecord.ps, self_.pid.take())
24
            {
24
                removed.auto_release(&mut state.global);
24
            }
24
            Some(())
24
        })();
        // self will be dropped now, but we have already cleared it out.
57570
    }
    /// Creates a new dangling, dummy, `Participation`
    ///
    /// This can be used as a standin where a value of type `Participation` is needed.
    /// The returned value cannot be used to claim memory,
    /// or find an `Account` or `MemoryQuotaTracker`.
57566
    pub fn new_dangling() -> Self {
57566
        let Some(enabled) = EnabledToken::new_if_compiled_in() else {
            return Participation(Noop);
        };
57566
        let inner = ParticipationInner {
57566
            pid: refcount::Ref::default(),
57566
            aid: AId::default(),
57566
            tracker: Weak::default(),
57566
            cache: ClaimedQty::ZERO,
57566
        };
57566
        Participation(Enabled(inner, enabled))
57566
    }
}
impl Clone for Participation {
79922
    fn clone(&self) -> Participation {
79922
        let Enabled(self_, enabled) = &self.0 else {
66654
            return Participation(Noop);
        };
13268
        let aid = self_.aid;
13268
        let cache = ClaimedQty::ZERO;
13268
        let tracker: Weak<_> = self_.tracker.clone();
19902
        let pid = (|| {
13268
            let pid = *self_.pid;
13268
            find_in_tracker! {
                enabled;
                self_.tracker => + tracker_strong, state;
13268
                aid => _arecord;
13264
                pid => precord;
                ?None
            }
13264
            let pid = refcount::Ref::new(pid, &mut precord.refcount).ok()?;
            // commitment point
13264
            Some(pid)
13268
        })()
13270
        .unwrap_or_else(|| {
4
            // The account has been closed, the participant torn down, or the refcount
4
            // overflowed.  We can a busted `Participation`.
4
            //
4
            // We *haven't* incremented the refcount, so we mustn't return pid as a strong
4
            // reference.  We aren't supposed to count towards PRecord.refcount, we we *can*
4
            // return the weak reference aid.  (`refcount` type-fu assures this is correct.)
4
            //
4
            // If the problem was refcount overflow, we're technically violating the
4
            // documented behaviour.  This is OK; see comment in `<Account as Clone>::clone`.
4
            refcount::Ref::null()
13270
        });
13268
        let inner = ParticipationInner {
13268
            aid,
13268
            pid,
13268
            cache,
13268
            tracker,
13268
        };
13268
        Participation(Enabled(inner, *enabled))
79922
    }
}
impl Drop for Participation {
195202
    fn drop(&mut self) {
195202
        let Enabled(self_, enabled) = &mut self.0 else {
124200
            return;
        };
71002
        (|| {
71002
            find_in_tracker! {
                enabled;
                self_.tracker => + tracker_strong, state;
13428
                self_.aid => arecord;
13272
                *self_.pid => precord;
                ?None
            }
            // release the cached claim
13240
            let from_cache = self_.cache.take();
13240
            state.global.total_used.release(precord, from_cache);
            if let Some(refcount::Garbage(mut removed)) =
13240
                slotmap_dec_ref!(&mut arecord.ps, self_.pid.take(), &mut precord.refcount)
            {
                // We might not have called `release` on everything, so we do that here.
                removed.auto_release(&mut state.global);
13240
            }
13240
            Some(())
71002
        })()
71944
        .unwrap_or_else(|| {
57762
            // Account or Participation or tracker destroyed.
57762
            // (This has no effect except in cfg(test), when it defuses the drop bombs)
57762
            self_.pid.take().dispose_container_destroyed();
57762
            self_.cache.take().dispose_participant_destroyed();
71944
        });
195202
    }
}
//========== impls on internal types ==========
impl State {
    /// Obtain all of the descendants of `parent_aid` according to the Child relation
    ///
    /// The returned `HashSet` includes `parent_aid`, its children,
    /// their children, and so on.
    ///
    /// Used in the reclamation algorithm in [`reclaim`].
52
    fn get_aid_and_children_recursively(&self, parent_aid: AId) -> HashSet<AId> {
52
        let mut out = HashSet::<AId>::new();
52
        let mut queue: Vec<AId> = vec![parent_aid];
108
        while let Some(aid) = queue.pop() {
56
            let Some(arecord) = self.accounts.get(aid) else {
                // shouldn't happen but no need to panic
                continue;
            };
56
            if out.insert(aid) {
56
                queue.extend(arecord.children.iter().cloned());
56
            }
        }
52
        out
52
    }
}
impl ARecord {
    /// Release all memory that this account's participants claimed
156
    fn auto_release(&mut self, global: &mut Global) {
156
        for (_pid, mut precord) in self.ps.drain() {
140
            precord.auto_release(global);
140
        }
156
    }
}
impl PRecord {
    /// Release all memory that this participant claimed
168
    fn auto_release(&mut self, global: &mut Global) {
168
        let for_teardown = self.used.for_participant_teardown();
168
        global.total_used.release(self, for_teardown);
168
    }
}