Skip to main content

netstack3_base/
resource_references.rs

1// Copyright 2024 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Helper types, traits, and aliases for dealing with resource references.
6
7use crate::sync::{DynDebugReferences, MapRcNotifier, PrimaryRc, RcNotifier};
8
9/// A context trait determining the types to be used for reference
10/// notifications.
11pub trait ReferenceNotifiers {
12    /// The receiver for shared reference destruction notifications.
13    type ReferenceReceiver<T: 'static>: 'static;
14    /// The notifier for shared reference destruction notifications.
15    type ReferenceNotifier<T: Send + 'static>: RcNotifier<T> + 'static;
16
17    /// Creates a new Notifier/Receiver pair for `T`.
18    ///
19    /// `debug_references` is given to provide information on outstanding
20    /// references that caused the notifier to be requested.
21    fn new_reference_notifier<T: Send + 'static>(
22        debug_references: DynDebugReferences,
23    ) -> (Self::ReferenceNotifier<T>, Self::ReferenceReceiver<T>);
24}
25
26/// A context trait that allows core to defer observing proper resource cleanup
27/// to bindings.
28///
29/// This trait exists as a debug utility to expose resources that are not
30/// removed from the system as expected.
31pub trait DeferredResourceRemovalContext: ReferenceNotifiers {
32    /// Defers the removal of some resource `T` to bindings.
33    ///
34    /// Bindings can watch `receiver` and notify when resource removal is not
35    /// completing in a timely manner.
36    fn defer_removal<T: Send + 'static>(&mut self, receiver: Self::ReferenceReceiver<T>);
37
38    /// A shorthand for [`defer_removal`] that takes a `ReferenceReceiver` from
39    /// the `Deferred` variant of a [`RemoveResourceResult`].
40    ///
41    /// The default implementation is `track_caller` when the `instrumented`
42    /// feature is available so implementers can track the caller location when
43    /// needed.
44    #[cfg_attr(feature = "instrumented", track_caller)]
45    fn defer_removal_result<T: Send + 'static>(
46        &mut self,
47        result: RemoveResourceResultWithContext<T, Self>,
48    ) {
49        match result {
50            // We don't need to do anything for synchronously removed resources.
51            RemoveResourceResult::Removed(_) => (),
52            RemoveResourceResult::Deferred(d) => self.defer_removal(d),
53        }
54    }
55}
56
57/// The result of removing some reference-counted resource from core.
58#[derive(Debug)]
59pub enum RemoveResourceResult<R, D> {
60    /// The resource was synchronously removed and no more references to it
61    /// exist.
62    Removed(R),
63    /// The resource was marked for destruction but there are still references
64    /// to it in existence. The provided receiver can be polled on to observe
65    /// resource destruction completion.
66    Deferred(D),
67}
68
69impl<R, D> RemoveResourceResult<R, D> {
70    /// Maps the `Removed` variant to a different type.
71    pub fn map_removed<N, F: FnOnce(R) -> N>(self, f: F) -> RemoveResourceResult<N, D> {
72        match self {
73            Self::Removed(r) => RemoveResourceResult::Removed(f(r)),
74            Self::Deferred(d) => RemoveResourceResult::Deferred(d),
75        }
76    }
77
78    /// Maps the `Deferred` variant to a different type.
79    pub fn map_deferred<N, F: FnOnce(D) -> N>(self, f: F) -> RemoveResourceResult<R, N> {
80        match self {
81            Self::Removed(r) => RemoveResourceResult::Removed(r),
82            Self::Deferred(d) => RemoveResourceResult::Deferred(f(d)),
83        }
84    }
85}
86
87impl<R> RemoveResourceResult<R, !> {
88    /// A helper function to unwrap a [`RemoveResourceResult`] that can never be
89    /// [`RemoveResourceResult::Deferred`].
90    pub fn into_removed(self) -> R {
91        match self {
92            Self::Removed(r) => r,
93        }
94    }
95}
96
97/// An alias for [`RemoveResourceResult`] that extracts the receiver type from
98/// the bindings context.
99pub type RemoveResourceResultWithContext<S, BT> =
100    RemoveResourceResult<S, <BT as ReferenceNotifiers>::ReferenceReceiver<S>>;
101
102/// An extension of [`ReferenceNotifiers`] providing extra functionality.
103pub trait ReferenceNotifiersExt: ReferenceNotifiers {
104    /// Unwraps a [`PrimaryRc`] if it has no pending references, otherwise
105    /// notifying with [`Self::ReferenceNotifier`].
106    ///
107    /// S: Is the state held behind the [`PrimaryRc`].
108    /// O: Is the desired output state, once references have been destructed.
109    /// F: Is a function that converts S to O.
110    fn unwrap_or_notify_with_new_reference_notifier<
111        S: Send + Sync + 'static,
112        O: Send,
113        F: Send + Clone + 'static + FnOnce(S) -> O,
114    >(
115        primary: PrimaryRc<S>,
116        map: F,
117    ) -> RemoveResourceResultWithContext<O, Self> {
118        let debug_references = PrimaryRc::debug_references(&primary).into_dyn();
119        let map_clone = map.clone();
120        match PrimaryRc::unwrap_or_notify_with(primary, move || {
121            let (notifier, receiver) = Self::new_reference_notifier::<O>(debug_references);
122            let notifier = MapRcNotifier::new(notifier, map_clone);
123            (notifier, receiver)
124        }) {
125            Ok(state) => RemoveResourceResult::Removed(map(state)),
126            Err(receiver) => RemoveResourceResult::Deferred(receiver),
127        }
128    }
129}
130
131impl<N: ReferenceNotifiers> ReferenceNotifiersExt for N {}