vigil/
vigil.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 std::fmt::{self, Debug};
6use std::ops::Deref;
7use std::sync::Mutex;
8
9use crate::DropWatch;
10
11/// Vigil is a smart pointer that implements DropWatch for any type.
12pub struct Vigil<T: ?Sized> {
13    ptr: Box<VigilInner<T>>,
14}
15
16impl<T: ?Sized> Vigil<T> {
17    fn from_inner(ptr: Box<VigilInner<T>>) -> Self {
18        Self { ptr }
19    }
20
21    fn inner(&self) -> &VigilInner<T> {
22        &self.ptr
23    }
24}
25
26impl<T> Vigil<T> {
27    pub fn new(data: T) -> Vigil<T> {
28        let x = Box::new(VigilInner { observers: Mutex::new(Vec::new()), data });
29        Self::from_inner(x)
30    }
31}
32
33impl<T: ?Sized> DropWatch<T> for Vigil<T> {
34    fn watch(this: &Self, drop_fn: impl FnOnce(&T) + 'static) {
35        this.inner().observers.lock().unwrap().push(Box::new(drop_fn));
36    }
37}
38
39#[derive(Default)]
40struct VigilInner<T: ?Sized> {
41    observers: Mutex<Vec<Box<dyn FnOnce(&T)>>>,
42    data: T,
43}
44
45impl<T: ?Sized> Drop for Vigil<T> {
46    fn drop(&mut self) {
47        for rite in self.inner().observers.lock().unwrap().drain(..) {
48            (rite)(&**self);
49        }
50    }
51}
52
53impl<T: ?Sized> Deref for Vigil<T> {
54    type Target = T;
55
56    fn deref(&self) -> &Self::Target {
57        &self.inner().data
58    }
59}
60
61impl<T: fuchsia_inspect_derive::Unit> fuchsia_inspect_derive::Unit for Vigil<T> {
62    type Data = T::Data;
63    fn inspect_create(&self, parent: &fuchsia_inspect::Node, name: impl AsRef<str>) -> Self::Data {
64        self.inner().data.inspect_create(parent, name)
65    }
66
67    fn inspect_update(&self, data: &mut Self::Data) {
68        self.inner().data.inspect_update(data)
69    }
70}
71
72impl<T: Debug> Debug for Vigil<T> {
73    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74        write!(f, "Vigil({:?})", self.inner().data)
75    }
76}
77
78#[cfg(test)]
79mod tests {
80    use std::rc::Rc;
81    use std::sync::atomic::{AtomicUsize, Ordering};
82
83    use super::*;
84
85    #[test]
86    fn rites_upon_drop() {
87        let ghosts = Rc::new(Mutex::new(String::new()));
88
89        let v = Vigil::new('👻');
90
91        Vigil::watch(&v, {
92            let ghosts = ghosts.clone();
93            move |thing| ghosts.lock().unwrap().push(thing.clone())
94        });
95
96        Vigil::watch(&v, {
97            let ghosts = ghosts.clone();
98            move |thing| ghosts.lock().unwrap().push(thing.clone())
99        });
100
101        drop(v);
102
103        // Dropping the vigil should have dropped all the Rc clones.
104        let unwrapped = Rc::try_unwrap(ghosts).unwrap();
105
106        let ghosts_result = unwrapped.into_inner().unwrap();
107
108        assert_eq!("👻👻", ghosts_result);
109    }
110    struct DropIncrementer(Rc<AtomicUsize>);
111
112    impl Drop for DropIncrementer {
113        fn drop(&mut self) {
114            let _ = self.0.fetch_add(1, Ordering::Relaxed);
115        }
116    }
117
118    #[test]
119    fn drops_stored_data() {
120        let shared_count = Rc::new(AtomicUsize::new(0));
121        let v = Vigil::new(DropIncrementer(shared_count.clone()));
122        drop(v);
123        assert_eq!(1, shared_count.load(Ordering::Relaxed));
124    }
125}