Skip to main content

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