fuchsia_inspect/writer/types/
node.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::private::InspectTypeInternal;
6use crate::writer::{
7    BoolProperty, BytesProperty, DoubleArrayProperty, DoubleExponentialHistogramProperty,
8    DoubleLinearHistogramProperty, DoubleProperty, Error, Inner, InnerData, InnerType, InspectType,
9    InspectTypeReparentable, Inspector, IntArrayProperty, IntExponentialHistogramProperty,
10    IntLinearHistogramProperty, IntProperty, LazyNode, State, StringArrayProperty, StringProperty,
11    UintArrayProperty, UintExponentialHistogramProperty, UintLinearHistogramProperty, UintProperty,
12    ValueList,
13};
14use diagnostics_hierarchy::{ArrayFormat, ExponentialHistogramParams, LinearHistogramParams};
15use futures::future::{BoxFuture, LocalBoxFuture};
16use futures::task::{Context, Poll};
17use inspect_format::{BlockIndex, LinkNodeDisposition};
18use std::borrow::Cow;
19use std::pin::Pin;
20use std::sync::atomic::{AtomicBool, Ordering};
21
22/// A wrapper for `LocalBoxFuture` that implements `Send`.
23///
24/// This is only safe to use in contexts where the future is guaranteed to be polled
25/// on the same thread it was created on. `fragile::Fragile` will panic if accessed
26/// from a different thread. This is useful for allowing `LocalBoxFuture` within
27/// closures that require `Send + Sync`, such as those used in `create_lazy_child`.
28struct SendFuture {
29    inner: fragile::Fragile<LocalBoxFuture<'static, Result<Inspector, anyhow::Error>>>,
30}
31
32impl futures::Future for SendFuture {
33    type Output = Result<Inspector, anyhow::Error>;
34
35    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
36        let mut pinned = std::pin::pin!(self.inner.get_mut());
37        pinned.as_mut().poll(cx)
38    }
39}
40
41/// Inspect Node data type.
42///
43/// NOTE: do not rely on PartialEq implementation for true comparison.
44/// Instead leverage the reader.
45///
46/// NOTE: Operations on a Default value are no-ops.
47#[derive(Debug, PartialEq, Eq, Default)]
48pub struct Node {
49    pub(crate) inner: Inner<InnerNodeType>,
50}
51
52impl InspectType for Node {}
53
54crate::impl_inspect_type_internal!(Node);
55
56impl Node {
57    /// Create a weak reference to the original node. All operations on a weak
58    /// reference have identical semantics to the original node for as long
59    /// as the original node is live. After that, all operations are no-ops.
60    pub fn clone_weak(&self) -> Node {
61        Self { inner: self.inner.clone_weak() }
62    }
63
64    /// Add a child to this node.
65    #[must_use]
66    pub fn create_child<'a>(&self, name: impl Into<Cow<'a, str>>) -> Node {
67        self.inner
68            .inner_ref()
69            .and_then(|inner_ref| {
70                inner_ref
71                    .state
72                    .try_lock()
73                    .and_then(|mut state| state.create_node(name.into(), inner_ref.block_index))
74                    .map(|block_index| Node::new(inner_ref.state.clone(), block_index))
75                    .ok()
76            })
77            .unwrap_or_else(Node::new_no_op)
78    }
79
80    /// Creates and keeps track of a child with the given `name`.
81    pub fn record_child<'a, F>(&self, name: impl Into<Cow<'a, str>>, initialize: F)
82    where
83        F: FnOnce(&Node),
84    {
85        self.atomic_update(move |n| {
86            let child = n.create_child(name.into());
87            initialize(&child);
88            n.record(child);
89        });
90    }
91
92    /// Takes a function to execute as under a single lock of the Inspect VMO. This function
93    /// receives a reference to the `Node` where this is called.
94    pub fn atomic_update<F, R>(&self, update_fn: F) -> R
95    where
96        F: FnOnce(&Node) -> R,
97    {
98        self.atomic_access(update_fn)
99    }
100
101    /// Keeps track of the given property for the lifetime of the node.
102    pub fn record(&self, property: impl InspectType + 'static) {
103        if let Some(inner_ref) = self.inner.inner_ref() {
104            inner_ref.data.values.record(property);
105        }
106    }
107
108    /// Drop all recorded data from the node.
109    pub fn clear_recorded(&self) {
110        if let Some(inner_ref) = self.inner.inner_ref() {
111            inner_ref.data.values.clear();
112        }
113    }
114
115    /// Creates a new `IntProperty` with the given `name` and `value`.
116    #[must_use]
117    pub fn create_int<'a>(&self, name: impl Into<Cow<'a, str>>, value: i64) -> IntProperty {
118        self.inner
119            .inner_ref()
120            .and_then(|inner_ref| {
121                inner_ref
122                    .state
123                    .try_lock()
124                    .and_then(|mut state| {
125                        state.create_int_metric(name.into(), value, inner_ref.block_index)
126                    })
127                    .map(|block_index| IntProperty::new(inner_ref.state.clone(), block_index))
128                    .ok()
129            })
130            .unwrap_or_else(IntProperty::new_no_op)
131    }
132
133    /// Records a new `IntProperty` with the given `name` and `value`.
134    pub fn record_int<'a>(&self, name: impl Into<Cow<'a, str>>, value: i64) {
135        let property = self.create_int(name.into(), value);
136        self.record(property);
137    }
138
139    /// Creates a new `UintProperty` with the given `name` and `value`.
140    #[must_use]
141    pub fn create_uint<'a>(&self, name: impl Into<Cow<'a, str>>, value: u64) -> UintProperty {
142        self.inner
143            .inner_ref()
144            .and_then(|inner_ref| {
145                inner_ref
146                    .state
147                    .try_lock()
148                    .and_then(|mut state| {
149                        state.create_uint_metric(name.into(), value, inner_ref.block_index)
150                    })
151                    .map(|block_index| UintProperty::new(inner_ref.state.clone(), block_index))
152                    .ok()
153            })
154            .unwrap_or_else(UintProperty::new_no_op)
155    }
156
157    /// Records a new `UintProperty` with the given `name` and `value`.
158    pub fn record_uint<'a>(&self, name: impl Into<Cow<'a, str>>, value: u64) {
159        let property = self.create_uint(name.into(), value);
160        self.record(property);
161    }
162
163    /// Creates a new `DoubleProperty` with the given `name` and `value`.
164    #[must_use]
165    pub fn create_double<'a>(&self, name: impl Into<Cow<'a, str>>, value: f64) -> DoubleProperty {
166        self.inner
167            .inner_ref()
168            .and_then(|inner_ref| {
169                inner_ref
170                    .state
171                    .try_lock()
172                    .and_then(|mut state| {
173                        state.create_double_metric(name.into(), value, inner_ref.block_index)
174                    })
175                    .map(|block_index| DoubleProperty::new(inner_ref.state.clone(), block_index))
176                    .ok()
177            })
178            .unwrap_or_else(DoubleProperty::new_no_op)
179    }
180
181    /// Records a new `DoubleProperty` with the given `name` and `value`.
182    pub fn record_double<'a>(&self, name: impl Into<Cow<'a, str>>, value: f64) {
183        let property = self.create_double(name.into(), value);
184        self.record(property);
185    }
186
187    /// Creates a new `StringArrayProperty` with the given `name` and `slots`.
188    #[must_use]
189    pub fn create_string_array<'a>(
190        &self,
191        name: impl Into<Cow<'a, str>>,
192        slots: usize,
193    ) -> StringArrayProperty {
194        self.inner
195            .inner_ref()
196            .and_then(|inner_ref| {
197                inner_ref
198                    .state
199                    .try_lock()
200                    .and_then(|mut state| {
201                        state.create_string_array(name.into(), slots, inner_ref.block_index)
202                    })
203                    .map(|block_index| {
204                        StringArrayProperty::new(inner_ref.state.clone(), block_index)
205                    })
206                    .ok()
207            })
208            .unwrap_or_else(StringArrayProperty::new_no_op)
209    }
210
211    /// Creates a new `IntArrayProperty` with the given `name` and `slots`.
212    #[must_use]
213    pub fn create_int_array<'a>(
214        &self,
215        name: impl Into<Cow<'a, str>>,
216        slots: usize,
217    ) -> IntArrayProperty {
218        self.create_int_array_internal(name.into(), slots, ArrayFormat::Default)
219    }
220
221    #[must_use]
222    pub(crate) fn create_int_array_internal<'a>(
223        &self,
224        name: impl Into<Cow<'a, str>>,
225        slots: usize,
226        format: ArrayFormat,
227    ) -> IntArrayProperty {
228        self.inner
229            .inner_ref()
230            .and_then(|inner_ref| {
231                inner_ref
232                    .state
233                    .try_lock()
234                    .and_then(|mut state| {
235                        state.create_int_array(name.into(), slots, format, inner_ref.block_index)
236                    })
237                    .map(|block_index| IntArrayProperty::new(inner_ref.state.clone(), block_index))
238                    .ok()
239            })
240            .unwrap_or_else(IntArrayProperty::new_no_op)
241    }
242
243    /// Creates a new `UintArrayProperty` with the given `name` and `slots`.
244    #[must_use]
245    pub fn create_uint_array<'a>(
246        &self,
247        name: impl Into<Cow<'a, str>>,
248        slots: usize,
249    ) -> UintArrayProperty {
250        self.create_uint_array_internal(name.into(), slots, ArrayFormat::Default)
251    }
252
253    #[must_use]
254    pub(crate) fn create_uint_array_internal<'a>(
255        &self,
256        name: impl Into<Cow<'a, str>>,
257        slots: usize,
258        format: ArrayFormat,
259    ) -> UintArrayProperty {
260        self.inner
261            .inner_ref()
262            .and_then(|inner_ref| {
263                inner_ref
264                    .state
265                    .try_lock()
266                    .and_then(|mut state| {
267                        state.create_uint_array(name.into(), slots, format, inner_ref.block_index)
268                    })
269                    .map(|block_index| UintArrayProperty::new(inner_ref.state.clone(), block_index))
270                    .ok()
271            })
272            .unwrap_or_else(UintArrayProperty::new_no_op)
273    }
274
275    /// Creates a new `DoubleArrayProperty` with the given `name` and `slots`.
276    #[must_use]
277    pub fn create_double_array<'a>(
278        &self,
279        name: impl Into<Cow<'a, str>>,
280        slots: usize,
281    ) -> DoubleArrayProperty {
282        self.create_double_array_internal(name.into(), slots, ArrayFormat::Default)
283    }
284
285    #[must_use]
286    pub(crate) fn create_double_array_internal<'a>(
287        &self,
288        name: impl Into<Cow<'a, str>>,
289        slots: usize,
290        format: ArrayFormat,
291    ) -> DoubleArrayProperty {
292        self.inner
293            .inner_ref()
294            .and_then(|inner_ref| {
295                inner_ref
296                    .state
297                    .try_lock()
298                    .and_then(|mut state| {
299                        state.create_double_array(name.into(), slots, format, inner_ref.block_index)
300                    })
301                    .map(|block_index| {
302                        DoubleArrayProperty::new(inner_ref.state.clone(), block_index)
303                    })
304                    .ok()
305            })
306            .unwrap_or_else(DoubleArrayProperty::new_no_op)
307    }
308
309    /// Creates a new `IntLinearHistogramProperty` with the given `name` and `params`.
310    #[must_use]
311    pub fn create_int_linear_histogram<'a>(
312        &self,
313        name: impl Into<Cow<'a, str>>,
314        params: LinearHistogramParams<i64>,
315    ) -> IntLinearHistogramProperty {
316        IntLinearHistogramProperty::new(name.into(), params, self)
317    }
318
319    /// Creates a new `UintLinearHistogramProperty` with the given `name` and `params`.
320    #[must_use]
321    pub fn create_uint_linear_histogram<'a>(
322        &self,
323        name: impl Into<Cow<'a, str>>,
324        params: LinearHistogramParams<u64>,
325    ) -> UintLinearHistogramProperty {
326        UintLinearHistogramProperty::new(name.into(), params, self)
327    }
328
329    /// Creates a new `DoubleLinearHistogramProperty` with the given `name` and `params`.
330    #[must_use]
331    pub fn create_double_linear_histogram<'a>(
332        &self,
333        name: impl Into<Cow<'a, str>>,
334        params: LinearHistogramParams<f64>,
335    ) -> DoubleLinearHistogramProperty {
336        DoubleLinearHistogramProperty::new(name.into(), params, self)
337    }
338
339    /// Creates a new `IntExponentialHistogramProperty` with the given `name` and `params`.
340    #[must_use]
341    pub fn create_int_exponential_histogram<'a>(
342        &self,
343        name: impl Into<Cow<'a, str>>,
344        params: ExponentialHistogramParams<i64>,
345    ) -> IntExponentialHistogramProperty {
346        IntExponentialHistogramProperty::new(name.into(), params, self)
347    }
348
349    /// Creates a new `UintExponentialHistogramProperty` with the given `name` and `params`.
350    #[must_use]
351    pub fn create_uint_exponential_histogram<'a>(
352        &self,
353        name: impl Into<Cow<'a, str>>,
354        params: ExponentialHistogramParams<u64>,
355    ) -> UintExponentialHistogramProperty {
356        UintExponentialHistogramProperty::new(name.into(), params, self)
357    }
358
359    /// Creates a new `DoubleExponentialHistogramProperty` with the given `name` and `params`.
360    #[must_use]
361    pub fn create_double_exponential_histogram<'a>(
362        &self,
363        name: impl Into<Cow<'a, str>>,
364        params: ExponentialHistogramParams<f64>,
365    ) -> DoubleExponentialHistogramProperty {
366        DoubleExponentialHistogramProperty::new(name.into(), params, self)
367    }
368
369    /// Creates a new lazy child with the given `name` and `callback`.
370    /// `callback` will be invoked each time the component's Inspect is read.
371    /// `callback` is expected to create a new Inspector and return it;
372    /// its contents will be rooted at the intended location (the `self` node).
373    #[must_use]
374    pub fn create_lazy_child<'a, F>(&self, name: impl Into<Cow<'a, str>>, callback: F) -> LazyNode
375    where
376        F: Fn() -> BoxFuture<'static, Result<Inspector, anyhow::Error>> + Sync + Send + 'static,
377    {
378        self.inner
379            .inner_ref()
380            .and_then(|inner_ref| {
381                inner_ref
382                    .state
383                    .try_lock()
384                    .and_then(|mut state| {
385                        state.create_lazy_node(
386                            name.into(),
387                            inner_ref.block_index,
388                            LinkNodeDisposition::Child,
389                            callback,
390                        )
391                    })
392                    .map(|block_index| LazyNode::new(inner_ref.state.clone(), block_index))
393                    .ok()
394            })
395            .unwrap_or_else(LazyNode::new_no_op)
396    }
397
398    /// Records a new lazy child with the given `name` and `callback`.
399    /// `callback` will be invoked each time the component's Inspect is read.
400    /// `callback` is expected to create a new Inspector and return it;
401    /// its contents will be rooted at the intended location (the `self` node).
402    pub fn record_lazy_child<'a, F>(&self, name: impl Into<Cow<'a, str>>, callback: F)
403    where
404        F: Fn() -> BoxFuture<'static, Result<Inspector, anyhow::Error>> + Sync + Send + 'static,
405    {
406        let property = self.create_lazy_child(name.into(), callback);
407        self.record(property);
408    }
409
410    /// Creates a new lazy child with the given `name` and `callback`.
411    /// `callback` will be invoked each time the component's Inspect is read.
412    /// `callback` is expected to create a new Inspector and return it;
413    /// its contents will be rooted at the intended location (the `self` node).
414    ///
415    /// The inspector referenced by this function should not be sent between threads,
416    /// or it may panic.
417    #[must_use]
418    pub fn create_lazy_child_with_thread_local<'a, F>(
419        &self,
420        name: impl Into<Cow<'a, str>>,
421        callback: F,
422    ) -> LazyNode
423    where
424        F: Fn() -> LocalBoxFuture<'static, Result<Inspector, anyhow::Error>> + 'static,
425    {
426        let fragile_callback = fragile::Fragile::new(callback);
427        self.create_lazy_child(name, move || {
428            let cb = fragile_callback.get();
429            Box::pin(SendFuture { inner: fragile::Fragile::new(cb()) })
430        })
431    }
432
433    /// Records a new lazy child with the given `name` and `callback`.
434    /// `callback` will be invoked each time the component's Inspect is read.
435    /// `callback` is expected to create a new Inspector and return it;
436    /// its contents will be rooted at the intended location (the `self` node).
437    ///
438    /// The inspector referenced by this function should not be sent between threads,
439    /// or it may panic.
440    pub fn record_lazy_child_with_thread_local<'a, F>(
441        &self,
442        name: impl Into<Cow<'a, str>>,
443        callback: F,
444    ) where
445        F: Fn() -> LocalBoxFuture<'static, Result<Inspector, anyhow::Error>> + 'static,
446    {
447        let property = self.create_lazy_child_with_thread_local(name.into(), callback);
448        self.record(property);
449    }
450
451    /// Creates a new inline lazy node with the given `name` and `callback`.
452    /// `callback` will be invoked each time the component's Inspect is read.
453    /// `callback` is expected to create a new Inspector and return it;
454    /// its contents will be rooted at the intended location (the `self` node).
455    #[must_use]
456    pub fn create_lazy_values<'a, F>(&self, name: impl Into<Cow<'a, str>>, callback: F) -> LazyNode
457    where
458        F: Fn() -> BoxFuture<'static, Result<Inspector, anyhow::Error>> + Sync + Send + 'static,
459    {
460        self.inner
461            .inner_ref()
462            .and_then(|inner_ref| {
463                inner_ref
464                    .state
465                    .try_lock()
466                    .and_then(|mut state| {
467                        state.create_lazy_node(
468                            name.into(),
469                            inner_ref.block_index,
470                            LinkNodeDisposition::Inline,
471                            callback,
472                        )
473                    })
474                    .map(|block_index| LazyNode::new(inner_ref.state.clone(), block_index))
475                    .ok()
476            })
477            .unwrap_or_else(LazyNode::new_no_op)
478    }
479
480    /// Records a new inline lazy node with the given `name` and `callback`.
481    /// `callback` will be invoked each time the component's Inspect is read.
482    /// `callback` is expected to create a new Inspector and return it;
483    /// its contents will be rooted at the intended location (the `self` node).
484    pub fn record_lazy_values<'a, F>(&self, name: impl Into<Cow<'a, str>>, callback: F)
485    where
486        F: Fn() -> BoxFuture<'static, Result<Inspector, anyhow::Error>> + Sync + Send + 'static,
487    {
488        let property = self.create_lazy_values(name.into(), callback);
489        self.record(property);
490    }
491
492    /// Creates a new inline lazy node with the given `name` and `callback`.
493    /// `callback` will be invoked each time the component's Inspect is read.
494    /// `callback` is expected to create a new Inspector and return it;
495    /// its contents will be rooted at the intended location (the `self` node).
496    ///
497    /// The inspector referenced by this function should not be sent between threads,
498    /// or it may panic.
499    #[must_use]
500    pub fn create_lazy_values_with_thread_local<'a, F>(
501        &self,
502        name: impl Into<Cow<'a, str>>,
503        callback: F,
504    ) -> LazyNode
505    where
506        F: Fn() -> LocalBoxFuture<'static, Result<Inspector, anyhow::Error>> + 'static,
507    {
508        let fragile_callback = fragile::Fragile::new(callback);
509        self.create_lazy_values(name, move || {
510            let cb = fragile_callback.get();
511            Box::pin(SendFuture { inner: fragile::Fragile::new(cb()) })
512        })
513    }
514
515    /// Records a new inline lazy node with the given `name` and `callback`.
516    /// `callback` will be invoked each time the component's Inspect is read.
517    /// `callback` is expected to create a new Inspector and return it;
518    /// its contents will be rooted at the intended location (the `self` node).
519    ///
520    /// The inspector referenced by this function should not be sent between threads,
521    /// or it may panic.
522    pub fn record_lazy_values_with_thread_local<'a, F>(
523        &self,
524        name: impl Into<Cow<'a, str>>,
525        callback: F,
526    ) where
527        F: Fn() -> LocalBoxFuture<'static, Result<Inspector, anyhow::Error>> + 'static,
528    {
529        let property = self.create_lazy_values_with_thread_local(name.into(), callback);
530        self.record(property);
531    }
532
533    /// Add a string property to this node.
534    #[must_use]
535    pub fn create_string<'a, 'b>(
536        &self,
537        name: impl Into<Cow<'a, str>>,
538        value: impl Into<Cow<'b, str>>,
539    ) -> StringProperty {
540        self.inner
541            .inner_ref()
542            .and_then(|inner_ref| {
543                inner_ref
544                    .state
545                    .try_lock()
546                    .and_then(|mut state| {
547                        state.create_string(name.into(), value.into(), inner_ref.block_index)
548                    })
549                    .map(|block_index| StringProperty::new(inner_ref.state.clone(), block_index))
550                    .ok()
551            })
552            .unwrap_or_else(StringProperty::new_no_op)
553    }
554
555    /// Creates and saves a string property for the lifetime of the node.
556    pub fn record_string<'a, 'b>(
557        &self,
558        name: impl Into<Cow<'a, str>>,
559        value: impl Into<Cow<'b, str>>,
560    ) {
561        let property = self.create_string(name, value);
562        self.record(property);
563    }
564
565    /// Add a byte vector property to this node.
566    #[must_use]
567    pub fn create_bytes<'a>(
568        &self,
569        name: impl Into<Cow<'a, str>>,
570        value: impl AsRef<[u8]>,
571    ) -> BytesProperty {
572        self.inner
573            .inner_ref()
574            .and_then(|inner_ref| {
575                inner_ref
576                    .state
577                    .try_lock()
578                    .and_then(|mut state| {
579                        state.create_buffer_property(
580                            name.into(),
581                            value.as_ref(),
582                            inner_ref.block_index,
583                        )
584                    })
585                    .map(|block_index| BytesProperty::new(inner_ref.state.clone(), block_index))
586                    .ok()
587            })
588            .unwrap_or_else(BytesProperty::new_no_op)
589    }
590
591    /// Creates and saves a bytes property for the lifetime of the node.
592    pub fn record_bytes<'a>(&self, name: impl Into<Cow<'a, str>>, value: impl AsRef<[u8]>) {
593        let property = self.create_bytes(name.into(), value);
594        self.record(property);
595    }
596
597    /// Add a bool property to this node.
598    #[must_use]
599    pub fn create_bool<'a>(&self, name: impl Into<Cow<'a, str>>, value: bool) -> BoolProperty {
600        self.inner
601            .inner_ref()
602            .and_then(|inner_ref| {
603                inner_ref
604                    .state
605                    .try_lock()
606                    .and_then(|mut state| {
607                        state.create_bool(name.into(), value, inner_ref.block_index)
608                    })
609                    .map(|block_index| BoolProperty::new(inner_ref.state.clone(), block_index))
610                    .ok()
611            })
612            .unwrap_or_else(BoolProperty::new_no_op)
613    }
614
615    /// Creates and saves a bool property for the lifetime of the node.
616    pub fn record_bool<'a>(&self, name: impl Into<Cow<'a, str>>, value: bool) {
617        let property = self.create_bool(name.into(), value);
618        self.record(property);
619    }
620
621    /// Takes a child from its parent and adopts it into its own tree.
622    pub fn adopt<T: InspectTypeReparentable>(&self, child: &T) -> Result<(), Error> {
623        child.reparent(self)
624    }
625
626    /// Removes this node from the Inspect tree. Typically, just dropping the Node must be
627    /// preferred. However, this is a convenience method meant for power user implementations that
628    /// need more control over the lifetime of a Node. For example, by being able to remove the node
629    /// from a weak clone of it.
630    pub fn forget(&self) {
631        if let Some(inner_ref) = self.inner.inner_ref() {
632            let _ = InnerNodeType::free(&inner_ref.state, &inner_ref.data, inner_ref.block_index);
633        }
634    }
635
636    /// Creates a new root node.
637    pub(crate) fn new_root(state: State) -> Node {
638        Node::new(state, BlockIndex::ROOT)
639    }
640}
641
642#[derive(Default, Debug)]
643pub(crate) struct InnerNodeType;
644
645#[derive(Default, Debug)]
646pub(crate) struct NodeData {
647    values: ValueList,
648    destroyed: AtomicBool,
649}
650
651impl InnerData for NodeData {
652    fn is_valid(&self) -> bool {
653        !self.destroyed.load(Ordering::SeqCst)
654    }
655}
656
657impl InnerType for InnerNodeType {
658    // Each node has a list of recorded values.
659    type Data = NodeData;
660
661    fn free(state: &State, data: &Self::Data, block_index: BlockIndex) -> Result<(), Error> {
662        if block_index == BlockIndex::ROOT {
663            return Ok(());
664        }
665        let mut state_lock = state.try_lock()?;
666        if data.destroyed.swap(true, Ordering::SeqCst) {
667            return Ok(());
668        }
669        state_lock.free_value(block_index).map_err(|err| Error::free("node", block_index, err))
670    }
671}
672
673#[cfg(test)]
674mod tests {
675    use super::*;
676    use crate::reader;
677    use crate::writer::ArrayProperty;
678    use crate::writer::testing_utils::{GetBlockExt, get_state};
679    use diagnostics_assertions::{assert_data_tree, assert_json_diff};
680    use futures::FutureExt;
681    use inspect_format::BlockType;
682
683    #[fuchsia::test]
684    fn node() {
685        // Create and use a default value.
686        let default = Node::default();
687        default.record_int("a", 0);
688
689        let state = get_state(4096);
690        let root = Node::new_root(state);
691        let node = root.create_child("node");
692        node.get_block::<_, inspect_format::Node>(|node_block| {
693            assert_eq!(node_block.block_type(), Some(BlockType::NodeValue));
694            assert_eq!(node_block.child_count(), 0);
695        });
696        {
697            let child = node.create_child("child");
698            child.get_block::<_, inspect_format::Node>(|child_block| {
699                assert_eq!(child_block.block_type(), Some(BlockType::NodeValue));
700                assert_eq!(child_block.child_count(), 0);
701            });
702            node.get_block::<_, inspect_format::Node>(|node_block| {
703                assert_eq!(node_block.child_count(), 1);
704            });
705        }
706        node.get_block::<_, inspect_format::Node>(|node_block| {
707            assert_eq!(node_block.child_count(), 0);
708        });
709    }
710
711    #[fuchsia::test]
712    async fn lazy_child() {
713        let inspector = Inspector::default();
714        let _lazy = inspector.root().create_lazy_child("lazy-1", || {
715            async move {
716                let insp = Inspector::default();
717                insp.root().record_lazy_child("parent", || {
718                    async move {
719                        let insp2 = Inspector::default();
720                        insp2.root().record_int("create-lazy-child", 0);
721                        insp2.root().record_int("create-lazy-child-2", 2);
722                        Ok(insp2)
723                    }
724                    .boxed()
725                });
726                Ok(insp)
727            }
728            .boxed()
729        });
730
731        inspector.root().record_lazy_child("lazy-2", || {
732            async move {
733                let insp = Inspector::default();
734                insp.root().record_bool("recorded-lazy-child", true);
735                Ok(insp)
736            }
737            .boxed()
738        });
739
740        inspector.root().record_lazy_values("lazy", || {
741            async move {
742                let insp = Inspector::default();
743                insp.root().record_bool("recorded-lazy-values", true);
744                Ok(insp)
745            }
746            .boxed()
747        });
748
749        let result = reader::read(&inspector).await.unwrap();
750
751        assert_data_tree!(result, root: {
752            "lazy-1": {
753                "parent": {
754                    "create-lazy-child": 0i64,
755                    "create-lazy-child-2": 2i64,
756                },
757            },
758            "lazy-2": {
759                "recorded-lazy-child": true,
760            },
761            "recorded-lazy-values": true,
762        });
763    }
764
765    #[fuchsia::test]
766    async fn lazy_child_with_thread_local() {
767        let inspector = Inspector::default();
768        inspector.root().record_lazy_child_with_thread_local("lazy-1", || {
769            async move {
770                let insp = Inspector::default();
771                insp.root().record_int("a", 1i64);
772                Ok(insp)
773            }
774            .boxed()
775        });
776
777        let result = reader::read(&inspector).await.unwrap();
778        assert_data_tree!(result, root: {
779            "lazy-1": {
780                "a": 1i64,
781            },
782        });
783    }
784
785    #[fuchsia::test]
786    async fn lazy_values_with_thread_local() {
787        let inspector = Inspector::default();
788        inspector.root().record_lazy_values_with_thread_local("lazy-1", || {
789            async move {
790                let insp = Inspector::default();
791                insp.root().record_int("a", 1i64);
792                Ok(insp)
793            }
794            .boxed()
795        });
796
797        let result = reader::read(&inspector).await.unwrap();
798        assert_data_tree!(result, root: {
799            "a": 1i64,
800        });
801    }
802
803    #[fuchsia::test]
804    async fn test_adoption() {
805        let insp = Inspector::default();
806        let root = insp.root();
807        let a = root.create_child("a");
808        let b = root.create_child("b");
809        let c = b.create_child("c");
810
811        assert_data_tree!(insp, root: {
812            a: {},
813            b: {
814                c: {},
815            },
816        });
817
818        a.adopt(&b).unwrap();
819
820        assert_data_tree!(insp, root: {
821            a: {
822                b: {
823                    c: {},
824                },
825            },
826        });
827
828        assert!(c.adopt(&a).is_err());
829        assert!(c.adopt(&b).is_err());
830        assert!(b.adopt(&a).is_err());
831        assert!(a.adopt(root).is_err());
832        assert!(a.adopt(&a).is_err());
833
834        {
835            let d = root.create_int("d", 4);
836
837            assert_data_tree!(insp, root: {
838                a: {
839                    b: {
840                        c: {},
841                    },
842                },
843                d: 4i64,
844            });
845
846            c.adopt(&d).unwrap();
847
848            assert_data_tree!(insp, root: {
849                a: {
850                    b: {
851                        c: {
852                            d: 4i64,
853                        },
854                    },
855                },
856            });
857        }
858
859        assert_data_tree!(insp, root: {
860            a: {
861                b: {
862                    c: {},
863                },
864            },
865        });
866    }
867
868    #[fuchsia::test]
869    fn node_no_op_clone_weak() {
870        let default = Node::default();
871        assert!(!default.is_valid());
872        let weak = default.clone_weak();
873        assert!(!weak.is_valid());
874        let _ = weak.create_child("child");
875        std::mem::drop(default);
876        let _ = weak.create_uint("age", 1337);
877        assert!(!weak.is_valid());
878    }
879
880    #[fuchsia::test]
881    fn node_clone_weak() {
882        let state = get_state(4096);
883        let root = Node::new_root(state);
884        let node = root.create_child("node");
885        let node_weak = node.clone_weak();
886        let node_weak_2 = node_weak.clone_weak(); // Weak from another weak
887
888        node.get_block::<_, inspect_format::Node>(|node_block| {
889            assert_eq!(node_block.block_type(), Some(BlockType::NodeValue));
890            assert_eq!(node_block.child_count(), 0);
891        });
892
893        let child_from_strong = node.create_child("child");
894        let child = node_weak.create_child("child_1");
895        let child_2 = node_weak_2.create_child("child_2");
896        std::mem::drop(node_weak_2);
897        node.get_block::<_, inspect_format::Node>(|block| {
898            assert_eq!(block.child_count(), 3);
899        });
900        std::mem::drop(child_from_strong);
901        node.get_block::<_, inspect_format::Node>(|block| {
902            assert_eq!(block.child_count(), 2);
903        });
904        std::mem::drop(child);
905        node.get_block::<_, inspect_format::Node>(|block| {
906            assert_eq!(block.child_count(), 1);
907        });
908        assert!(node_weak.is_valid());
909        assert!(child_2.is_valid());
910        std::mem::drop(node);
911        assert!(!node_weak.is_valid());
912        let _ = node_weak.create_child("orphan");
913        let _ = child_2.create_child("orphan");
914    }
915
916    #[fuchsia::test]
917    fn dummy_partialeq() {
918        let inspector = Inspector::default();
919        let root = inspector.root();
920
921        // Types should all be equal to another type. This is to enable clients
922        // with inspect types in their structs be able to derive PartialEq and
923        // Eq smoothly.
924        assert_eq!(root, &root.create_child("child1"));
925        assert_eq!(root.create_int("property1", 1), root.create_int("property2", 2));
926        assert_eq!(root.create_double("property1", 1.0), root.create_double("property2", 2.0));
927        assert_eq!(root.create_uint("property1", 1), root.create_uint("property2", 2));
928        assert_eq!(
929            root.create_string("property1", "value1"),
930            root.create_string("property2", "value2")
931        );
932        assert_eq!(
933            root.create_bytes("property1", b"value1"),
934            root.create_bytes("property2", b"value2")
935        );
936    }
937
938    #[fuchsia::test]
939    async fn record() {
940        let inspector = Inspector::default();
941        let property = inspector.root().create_uint("a", 1);
942        inspector.root().record_uint("b", 2);
943        {
944            let child = inspector.root().create_child("child");
945            child.record(property);
946            child.record_double("c", 3.25);
947            assert_data_tree!(inspector, root: {
948                a: 1u64,
949                b: 2u64,
950                child: {
951                    c: 3.25,
952                }
953            });
954        }
955        // `child` went out of scope, meaning it was deleted.
956        // Property `a` should be gone as well, given that it was being tracked by `child`.
957        assert_data_tree!(inspector, root: {
958            b: 2u64,
959        });
960
961        inspector.root().clear_recorded();
962        assert_data_tree!(inspector, root: {});
963    }
964
965    #[fuchsia::test]
966    async fn clear_recorded() {
967        let inspector = Inspector::default();
968        let one = inspector.root().create_child("one");
969        let two = inspector.root().create_child("two");
970        let one_recorded = one.create_child("one_recorded");
971        let two_recorded = two.create_child("two_recorded");
972
973        one.record(one_recorded);
974        two.record(two_recorded);
975
976        assert_json_diff!(inspector, root: {
977            one: {
978                one_recorded: {},
979            },
980            two: {
981                two_recorded: {},
982            },
983        });
984
985        two.clear_recorded();
986
987        assert_json_diff!(inspector, root: {
988            one: {
989                one_recorded: {},
990            },
991            two: {},
992        });
993
994        one.clear_recorded();
995
996        assert_json_diff!(inspector, root: {
997            one: {},
998            two: {},
999        });
1000    }
1001
1002    #[fuchsia::test]
1003    async fn record_child() {
1004        let inspector = Inspector::default();
1005        inspector.root().record_child("test", |node| {
1006            node.record_int("a", 1);
1007        });
1008        assert_data_tree!(inspector, root: {
1009            test: {
1010                a: 1i64,
1011            }
1012        })
1013    }
1014
1015    #[fuchsia::test]
1016    async fn record_weak() {
1017        let inspector = Inspector::default();
1018        let main = inspector.root().create_child("main");
1019        let main_weak = main.clone_weak();
1020        let property = main_weak.create_uint("a", 1);
1021
1022        // Ensure either the weak or strong reference can be used for recording
1023        main_weak.record_uint("b", 2);
1024        main.record_uint("c", 3);
1025        {
1026            let child = main_weak.create_child("child");
1027            child.record(property);
1028            child.record_double("c", 3.25);
1029            assert_data_tree!(inspector, root: { main: {
1030                a: 1u64,
1031                b: 2u64,
1032                c: 3u64,
1033                child: {
1034                    c: 3.25,
1035                }
1036            }});
1037        }
1038        // `child` went out of scope, meaning it was deleted.
1039        // Property `a` should be gone as well, given that it was being tracked by `child`.
1040        assert_data_tree!(inspector, root: { main: {
1041            b: 2u64,
1042            c: 3u64
1043        }});
1044        std::mem::drop(main);
1045        // Recording after dropping a strong reference is a no-op
1046        main_weak.record_double("d", 1.0);
1047        // Verify that dropping a strong reference cleans up the state
1048        assert_data_tree!(inspector, root: { });
1049    }
1050
1051    #[fuchsia::test]
1052    async fn string_arrays_on_record() {
1053        let inspector = Inspector::default();
1054        inspector.root().record_child("child", |node| {
1055            node.record_int("my_int", 1i64);
1056
1057            let arr: crate::StringArrayProperty = node.create_string_array("my_string_array", 1);
1058            arr.set(0, "test");
1059            node.record(arr);
1060        });
1061        assert_data_tree!(inspector, root: {
1062            child: {
1063                my_int: 1i64,
1064                my_string_array: vec!["test"]
1065            }
1066        });
1067    }
1068
1069    #[fuchsia::test]
1070    async fn we_can_delete_a_node_explicitly_with_the_weak_clone() {
1071        let insp = Inspector::default();
1072        let a = insp.root().create_child("a");
1073        let _property = a.create_int("foo", 1);
1074        assert_data_tree!(insp, root: {
1075            a: {
1076                foo: 1i64,
1077            }
1078        });
1079
1080        let a_weak = a.clone_weak();
1081        a_weak.forget();
1082        assert!(a.inner.inner_ref().is_none());
1083        assert_data_tree!(insp, root: {});
1084    }
1085}
1086
1087// Tests that either refer explicitly to VMOs or utilize zircon signals.
1088#[cfg(all(test, target_os = "fuchsia"))]
1089mod fuchsia_tests {
1090    use super::*;
1091    use crate::hierarchy::DiagnosticsHierarchy;
1092    use crate::{NumericProperty, reader};
1093    use diagnostics_assertions::assert_json_diff;
1094    use std::sync::Arc;
1095    use zx::{self as zx, AsHandleRef, Peered};
1096
1097    #[fuchsia::test]
1098    fn atomic_update_reader() {
1099        let inspector = Inspector::default();
1100
1101        // Spawn a read thread that holds a duplicate handle to the VMO that will be written.
1102        let vmo = Arc::new(inspector.duplicate_vmo().expect("duplicate vmo handle"));
1103        let (p1, p2) = zx::EventPair::create();
1104
1105        macro_rules! notify_and_wait_reader {
1106            () => {
1107                p1.signal_peer(zx::Signals::NONE, zx::Signals::USER_0).unwrap();
1108                p1.wait_handle(zx::Signals::USER_0, zx::MonotonicInstant::INFINITE).unwrap();
1109                p1.signal_handle(zx::Signals::USER_0, zx::Signals::NONE).unwrap();
1110            };
1111        }
1112
1113        macro_rules! wait_and_notify_writer {
1114            ($code:block) => {
1115              p2.wait_handle(zx::Signals::USER_0, zx::MonotonicInstant::INFINITE).unwrap();
1116              p2.signal_handle(zx::Signals::USER_0, zx::Signals::NONE).unwrap();
1117              $code
1118              p2.signal_peer(zx::Signals::NONE, zx::Signals::USER_0).unwrap();
1119            }
1120        }
1121
1122        let thread = std::thread::spawn(move || {
1123            // Before running the atomic update.
1124            wait_and_notify_writer! {{
1125                let hierarchy: DiagnosticsHierarchy<String> =
1126                    reader::PartialNodeHierarchy::try_from(vmo.as_ref()).unwrap().into();
1127                assert_eq!(hierarchy, DiagnosticsHierarchy::new_root());
1128            }};
1129            // After: create_child("child"): Assert that the VMO is in use (locked) and we can't
1130            // read.
1131            wait_and_notify_writer! {{
1132                assert!(reader::PartialNodeHierarchy::try_from(vmo.as_ref()).is_err());
1133            }};
1134            // After: record_int("a"): Assert that the VMO is in use (locked) and we can't
1135            // read.
1136            wait_and_notify_writer! {{
1137                assert!(reader::PartialNodeHierarchy::try_from(vmo.as_ref()).is_err());
1138            }};
1139            // After: record_int("b"): Assert that the VMO is in use (locked) and we can't
1140            // read.
1141            wait_and_notify_writer! {{
1142                assert!(reader::PartialNodeHierarchy::try_from(vmo.as_ref()).is_err());
1143            }};
1144            // After atomic update
1145            wait_and_notify_writer! {{
1146                let hierarchy: DiagnosticsHierarchy<String> =
1147                    reader::PartialNodeHierarchy::try_from(vmo.as_ref()).unwrap().into();
1148                // Attempting to make this whole lambda async and using Scope or similar
1149                // results in https://github.com/rust-lang/rust/issues/100013.
1150                fuchsia_async::TestExecutor::new().run_singlethreaded(async move {
1151                    assert_json_diff!(hierarchy, root: {
1152                       value: 2i64,
1153                       child: {
1154                           a: 1i64,
1155                           b: 2i64,
1156                       }
1157                    });
1158                });
1159            }};
1160        });
1161
1162        // Perform the atomic update
1163        let mut child = Node::default();
1164        notify_and_wait_reader!();
1165        let int_val = inspector.root().create_int("value", 1);
1166        inspector
1167            .root()
1168            .atomic_update(|node| {
1169                // Intentionally make this slow to assert an atomic update in the reader.
1170                child = node.create_child("child");
1171                notify_and_wait_reader!();
1172                child.record_int("a", 1);
1173                notify_and_wait_reader!();
1174                child.record_int("b", 2);
1175                notify_and_wait_reader!();
1176                int_val.add(1);
1177                Ok::<(), Error>(())
1178            })
1179            .expect("successful atomic update");
1180        notify_and_wait_reader!();
1181
1182        // Wait for the reader thread to successfully finish.
1183        let _ = thread.join();
1184
1185        // Ensure that the variable that we mutated internally can be used.
1186        child.record_int("c", 3);
1187        fuchsia_async::TestExecutor::new().run_singlethreaded(async move {
1188            assert_json_diff!(inspector, root: {
1189                value: 2i64,
1190                child: {
1191                    a: 1i64,
1192                    b: 2i64,
1193                    c: 3i64,
1194                }
1195            });
1196        });
1197    }
1198}