1
//! Support for weak references.
2
//!
3
//! Currently, this is unused in Arti; we may eventually remove it, which will let us simplify our
4
//! code.
5
//!
6
//! In any case, we should not use this until we have a solid idea about how weak references should
7
//! behave; see #868.
8

            
9
#![allow(dead_code)]
10

            
11
use std::{
12
    any,
13
    sync::{Arc, Weak},
14
};
15

            
16
use super::{raw_addr_of, raw_addr_of_weak, GenIdx, ObjMap, TaggedAddr};
17
use tor_rpcbase as rpc;
18

            
19
/// A single entry to a weak Object stored in the generational arena.
20
pub(super) struct WeakArenaEntry {
21
    /// The actual Arc or Weak reference for the object that we're storing here.
22
    pub(super) obj: Weak<dyn rpc::Object>,
23
    ///
24
    /// This contains a strong or weak reference, along with the object's true TypeId.
25
    /// See the [`TaggedAddr`] for more info on
26
    /// why this is needed.
27
    id: any::TypeId,
28
}
29

            
30
impl WeakArenaEntry {
31
    /// Create a new `WeakArenaEntry` for a weak reference.
32
2212
    pub(super) fn new(object: &Arc<dyn rpc::Object>) -> Self {
33
2212
        let id = (**object).type_id();
34
2212
        Self {
35
2212
            obj: Arc::downgrade(object),
36
2212
            id,
37
2212
        }
38
2212
    }
39

            
40
    /// Return true if this `ArenaEntry` is really present.
41
    ///
42
    /// Note that this function can produce false positives (if the entry's
43
    /// last strong reference is dropped in another thread), but it can
44
    /// never produce false negatives.
45
3456
    pub(super) fn is_present(&self) -> bool {
46
3456
        // This is safe from false negatives because: if we can ever
47
3456
        // observe strong_count == 0, then there is no way for anybody
48
3456
        // else to "resurrect" the object.
49
3456
        self.obj.strong_count() > 0
50
3456
    }
51

            
52
    /// Return a strong reference to the object in this entry, if possible.
53
348
    pub(super) fn strong(&self) -> Option<Arc<dyn rpc::Object>> {
54
348
        Weak::upgrade(&self.obj)
55
348
    }
56

            
57
    /// Return the [`TaggedAddr`] that can be used to identify this entry's object.
58
64920
    pub(super) fn tagged_addr(&self) -> TaggedAddr {
59
64920
        TaggedAddr {
60
64920
            addr: raw_addr_of_weak(&self.obj),
61
64920
            type_id: self.id,
62
64920
        }
63
64920
    }
64
}
65

            
66
impl TaggedAddr {
67
    /// Return the `TaggedAddr` to uniquely identify `obj` over the course of
68
    /// its existence.
69
2246
    pub(super) fn for_object(obj: &Arc<dyn rpc::Object>) -> Self {
70
2246
        let type_id = (*obj).type_id();
71
2246
        let addr = raw_addr_of(obj);
72
2246
        TaggedAddr { addr, type_id }
73
2246
    }
74
}
75

            
76
impl ObjMap {
77
    /// Reclaim unused space in this map's weak arena.
78
    ///
79
    /// This runs in `O(n)` time.
80
56
    pub(super) fn tidy(&mut self) {
81
56
        #[cfg(test)]
82
56
        {
83
56
            self.n_tidies += 1;
84
56
        }
85
3484
        self.weak_arena.retain(|index, entry| {
86
3456
            let present = entry.is_present();
87
3456
            if !present {
88
                // For everything we are removing from the `arena`, we must also
89
                // remove it from `reverse_map`.
90
2000
                let ptr = entry.tagged_addr();
91
2000
                let found = self.reverse_map.remove(&ptr);
92
2000
                debug_assert_eq!(found, Some(index));
93
1456
            }
94
3456
            present
95
3484
        });
96
56
    }
97

            
98
    /// If needed, clean the weak arena and resize it.
99
    ///
100
    /// (We call this whenever we're about to add an entry.  This ensures that
101
    /// our insertion operations run in `O(1)` time.)
102
2212
    fn adjust_size(&mut self) {
103
2212
        // If we're about to fill the arena...
104
2212
        if self.weak_arena.len() >= self.weak_arena.capacity() {
105
            // ... we delete any dead `Weak` entries.
106
54
            self.tidy();
107
54
            // Then, if the arena is still above half-full, we double the
108
54
            // capacity of the arena.
109
54
            //
110
54
            // (We have to grow the arena this even if tidy() removed _some_
111
54
            // entries, or else we might re-run tidy() too soon.  But we don't
112
54
            // want to grow the arena if tidy() removed _most_ entries, or some
113
54
            // normal usage patterns will lead to unbounded growth.)
114
54
            if self.weak_arena.len() > self.weak_arena.capacity() / 2 {
115
12
                self.weak_arena.reserve(self.weak_arena.capacity());
116
42
            }
117
2158
        }
118
2212
    }
119

            
120
    /// Ensure that there is a weak entry for `value` in self, and return an
121
    /// index for it.
122
    /// If there is no entry, create a weak entry.
123
    #[allow(clippy::needless_pass_by_value)] // TODO: Decide whether to make this take a reference.
124
2214
    pub(crate) fn insert_weak(&mut self, value: Arc<dyn rpc::Object>) -> GenIdx {
125
2214
        let ptr = TaggedAddr::for_object(&value);
126
2214
        if let Some(idx) = self.reverse_map.get(&ptr) {
127
            #[cfg(debug_assertions)]
128
2
            match self.weak_arena.get(*idx) {
129
2
                Some(entry) => debug_assert!(entry.tagged_addr() == ptr),
130
                None => panic!("Found a dangling reference"),
131
            }
132
2
            return GenIdx::Weak(*idx);
133
2212
        }
134
2212

            
135
2212
        self.adjust_size();
136
2212
        let idx = self.weak_arena.insert(WeakArenaEntry::new(&value));
137
2212
        self.reverse_map.insert(ptr, idx);
138
2212
        GenIdx::Weak(idx)
139
2214
    }
140
}