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 {}