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