Skip to main content

fuchsia_inspect/writer/types/
base.rs

1// Copyright 2021 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
5use crate::writer::{Error, Node, State};
6use derivative::Derivative;
7use inspect_format::BlockIndex;
8use private::InspectTypeInternal;
9use std::fmt::Debug;
10use std::sync::{Arc, Weak};
11
12/// Trait implemented by all inspect types.
13pub trait InspectType: Send + Sync + Debug {
14    fn into_recorded(self) -> crate::writer::types::RecordedInspectType
15    where
16        Self: Sized + 'static;
17}
18
19pub(crate) mod private {
20    use crate::writer::State;
21    use inspect_format::BlockIndex;
22
23    /// Trait implemented by all inspect types. It provides constructor functions that are not
24    /// intended for use outside the crate.
25    /// Use `impl_inspect_type_internal` for easy implementation.
26    pub trait InspectTypeInternal {
27        fn new(state: State, block_index: BlockIndex) -> Self;
28        fn new_no_op() -> Self;
29        fn is_valid(&self) -> bool;
30        fn block_index(&self) -> Option<BlockIndex>;
31        fn state(&self) -> Option<State>;
32        fn atomic_access<R, F: FnOnce(&Self) -> R>(&self, accessor: F) -> R;
33    }
34}
35
36/// Trait allowing a `Node` to adopt any Inspect type as its child, removing
37/// it from the original parent's tree.
38///
39/// This trait is not implementable by external types.
40pub trait InspectTypeReparentable: private::InspectTypeInternal {
41    #[doc(hidden)]
42    /// This function is called by a child with the new parent as an argument.
43    /// The child will be removed from its current parent and added to the tree
44    /// under new_parent.
45    fn reparent(&self, new_parent: &Node) -> Result<(), Error> {
46        if let (
47            Some(child_state),
48            Some(child_index),
49            Some(new_parent_state),
50            Some(new_parent_index),
51        ) = (self.state(), self.block_index(), new_parent.state(), new_parent.block_index())
52        {
53            if new_parent_state != child_state {
54                return Err(Error::AdoptionIntoWrongVmo);
55            }
56
57            new_parent_state
58                .try_lock()
59                .and_then(|mut state| state.reparent(child_index, new_parent_index))?;
60        }
61
62        Ok(())
63    }
64}
65
66impl<T: private::InspectTypeInternal> InspectTypeReparentable for T {}
67
68/// Macro to generate private::InspectTypeInternal
69macro_rules! impl_inspect_type_internal {
70    ($type_name:ident) => {
71        impl $crate::private::InspectTypeInternal for $type_name {
72            fn new(
73                state: $crate::writer::State,
74                block_index: inspect_format::BlockIndex,
75            ) -> $type_name {
76                $type_name { inner: $crate::writer::types::base::Inner::new(state, block_index) }
77            }
78
79            fn is_valid(&self) -> bool {
80                self.inner.is_valid()
81            }
82
83            fn new_no_op() -> $type_name {
84                $type_name { inner: $crate::writer::types::base::Inner::None }
85            }
86
87            fn state(&self) -> Option<$crate::writer::State> {
88                Some(self.inner.inner_ref()?.state.clone())
89            }
90
91            fn block_index(&self) -> Option<inspect_format::BlockIndex> {
92                if let Some(ref inner_ref) = self.inner.inner_ref() {
93                    Some(inner_ref.block_index)
94                } else {
95                    None
96                }
97            }
98
99            fn atomic_access<R, F: FnOnce(&Self) -> R>(&self, accessor: F) -> R {
100                match self.inner.inner_ref() {
101                    None => {
102                        // If the node was a no-op we still execute the `accessor` even if all
103                        // operations inside it will be no-ops to return `R`.
104                        accessor(&self)
105                    }
106                    Some(inner_ref) => {
107                        // Silently ignore the error when fail to lock (as in any regular operation).
108                        // All operations performed in the `accessor` won't update the vmo
109                        // generation count since we'll be holding one lock here.
110                        inner_ref.state.begin_transaction();
111                        let result = accessor(&self);
112                        inner_ref.state.end_transaction();
113                        result
114                    }
115                }
116            }
117        }
118    };
119}
120
121pub(crate) use impl_inspect_type_internal;
122
123/// An inner type of all inspect nodes and properties. Each variant implies a
124/// different relationship with the underlying inspect VMO.
125#[derive(Debug, Derivative)]
126#[derivative(Default)]
127pub(crate) enum Inner<T: InnerType> {
128    /// The node or property is not attached to the inspect VMO.
129    #[derivative(Default)]
130    None,
131
132    /// The node or property is attached to the inspect VMO, iff its strong
133    /// reference is still alive.
134    Weak(Weak<InnerRef<T>>),
135
136    /// The node or property is attached to the inspect VMO.
137    Strong(Arc<InnerRef<T>>),
138}
139
140impl<T: InnerType> Inner<T> {
141    /// Creates a new Inner with the desired block index within the inspect VMO
142    pub(crate) fn new(state: State, block_index: BlockIndex) -> Self {
143        Self::Strong(Arc::new(InnerRef { state, block_index, data: T::Data::default() }))
144    }
145
146    /// Returns true if the number of strong references to this node or property
147    /// is greater than 0.
148    pub(crate) fn is_valid(&self) -> bool {
149        match self {
150            Self::None => false,
151            Self::Weak(weak_ref) => match weak_ref.upgrade() {
152                None => false,
153                Some(inner_ref) => inner_ref.data.is_valid(),
154            },
155            Self::Strong(inner_ref) => inner_ref.data.is_valid(),
156        }
157    }
158
159    /// Returns a `Some(Arc<InnerRef>)` iff the node or property is currently
160    /// attached to inspect, or `None` otherwise. Weak pointers are upgraded
161    /// if possible, but their lifetime as strong references are expected to be
162    /// short.
163    pub(crate) fn inner_ref(&self) -> Option<Arc<InnerRef<T>>> {
164        match self {
165            Self::None => None,
166            Self::Weak(weak_ref) => {
167                if let Some(inner_ref) = weak_ref.upgrade()
168                    && inner_ref.data.is_valid()
169                {
170                    return Some(inner_ref);
171                }
172                None
173            }
174            Self::Strong(inner_ref) => {
175                if inner_ref.data.is_valid() {
176                    Some(Arc::clone(inner_ref))
177                } else {
178                    None
179                }
180            }
181        }
182    }
183
184    /// Make a weak reference.
185    pub(crate) fn clone_weak(&self) -> Self {
186        match self {
187            Self::None => Self::None,
188            Self::Weak(weak_ref) => Self::Weak(weak_ref.clone()),
189            Self::Strong(inner_ref) => {
190                if inner_ref.data.is_valid() {
191                    Self::Weak(Arc::downgrade(inner_ref))
192                } else {
193                    Self::None
194                }
195            }
196        }
197    }
198}
199
200/// Inspect API types implement Eq,PartialEq returning true all the time so that
201/// structs embedding inspect types can derive these traits as well.
202/// IMPORTANT: Do not rely on these traits implementations for real comparisons
203/// or validation tests, instead leverage the reader.
204impl<T: InnerType> PartialEq for Inner<T> {
205    fn eq(&self, _other: &Self) -> bool {
206        true
207    }
208}
209
210impl<T: InnerType> Eq for Inner<T> {}
211
212/// A type that is owned by inspect nodes and properties, sharing ownership of
213/// the inspect VMO heap, and with numerical pointers to the location in the
214/// heap in which it resides.
215#[derive(Debug)]
216pub(crate) struct InnerRef<T: InnerType> {
217    /// Index of the block in the VMO.
218    pub(crate) block_index: BlockIndex,
219
220    /// Reference to the VMO heap.
221    pub(crate) state: State,
222
223    /// Associated data for this type.
224    pub(crate) data: T::Data,
225}
226
227impl<T: InnerType> Drop for InnerRef<T> {
228    /// InnerRef has a manual drop impl, to guarantee a single deallocation in
229    /// the case of multiple strong references.
230    fn drop(&mut self) {
231        T::free(&self.state, &self.data, self.block_index).unwrap();
232    }
233}
234
235/// De-allocation behavior and associated data for an inner type.
236pub(crate) trait InnerType {
237    /// Associated data stored on the InnerRef
238    type Data: Default + Debug + InnerData;
239
240    /// De-allocation behavior for when the InnerRef gets dropped
241    fn free(state: &State, data: &Self::Data, block_index: BlockIndex) -> Result<(), Error>;
242}
243
244pub(crate) trait InnerData {
245    fn is_valid(&self) -> bool;
246}
247
248impl InnerData for () {
249    fn is_valid(&self) -> bool {
250        true
251    }
252}
253
254#[derive(Default, Debug)]
255pub(crate) struct InnerValueType;
256
257impl InnerType for InnerValueType {
258    type Data = ();
259    fn free(state: &State, _: &Self::Data, block_index: BlockIndex) -> Result<(), Error> {
260        let mut state_lock = state.try_lock()?;
261        state_lock.free_value(block_index).map_err(|err| Error::free("value", block_index, err))
262    }
263}
264
265#[cfg(test)]
266mod tests {
267    use super::*;
268    use crate::Inspector;
269    use diagnostics_assertions::assert_data_tree;
270
271    #[fuchsia::test]
272    async fn test_reparent_from_state() {
273        let insp = Inspector::default();
274        let root = insp.root();
275        let a = root.create_child("a");
276        let b = a.create_child("b");
277
278        assert_data_tree!(insp, root: {
279            a: {
280                b: {},
281            },
282        });
283
284        b.reparent(root).unwrap();
285
286        assert_data_tree!(insp, root: {
287            b: {},
288            a: {},
289        });
290    }
291
292    #[fuchsia::test]
293    fn reparent_from_wrong_state() {
294        let insp1 = Inspector::default();
295        let insp2 = Inspector::default();
296
297        assert!(insp1.root().reparent(insp2.root()).is_err());
298
299        let a = insp1.root().create_child("a");
300        let b = insp2.root().create_child("b");
301
302        assert!(a.reparent(&b).is_err());
303        assert!(b.reparent(&a).is_err());
304    }
305}