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