Coverage Report

Created: 2025-02-25 06:39

/rust/registry/src/index.crates.io-6f17d22bba15001f/flurry-0.5.2/src/iter/traverser.rs
Line
Count
Source (jump to first uncovered line)
1
use crate::node::{BinEntry, Node, TreeNode};
2
use crate::raw::Table;
3
use crate::reclaim::{Guard, Linked, Shared};
4
use std::sync::atomic::Ordering;
5
6
#[derive(Debug)]
7
pub(crate) struct NodeIter<'g, K, V> {
8
    /// Current table; update if resized
9
    table: Option<&'g Linked<Table<K, V>>>,
10
11
    stack: Option<Box<TableStack<'g, K, V>>>,
12
    spare: Option<Box<TableStack<'g, K, V>>>,
13
14
    /// The last bin entry iterated over
15
    prev: Option<&'g Node<K, V>>,
16
17
    /// Index of bin to use next
18
    index: usize,
19
20
    /// Current index of initial table
21
    base_index: usize,
22
23
    /// Index bound for inital table
24
    base_limit: usize,
25
26
    /// Initial table size
27
    base_size: usize,
28
29
    guard: &'g Guard<'g>,
30
}
31
32
impl<'g, K, V> NodeIter<'g, K, V> {
33
0
    pub(crate) fn new(table: Shared<'g, Table<K, V>>, guard: &'g Guard<'_>) -> Self {
34
0
        let (table, len) = if table.is_null() {
35
0
            (None, 0)
36
        } else {
37
            // safety: flurry guarantees that a table read under a guard is never dropped or moved
38
            // until after that guard is dropped.
39
0
            let table = unsafe { table.deref() };
40
0
            (Some(table), table.len())
41
        };
42
43
0
        Self {
44
0
            table,
45
0
            stack: None,
46
0
            spare: None,
47
0
            prev: None,
48
0
            base_size: len,
49
0
            base_index: 0,
50
0
            index: 0,
51
0
            base_limit: len,
52
0
            guard,
53
0
        }
54
0
    }
55
56
0
    fn push_state(&mut self, t: &'g Linked<Table<K, V>>, i: usize, n: usize) {
57
0
        let mut s = self.spare.take();
58
0
        if let Some(ref mut s) = s {
59
0
            self.spare = s.next.take();
60
0
        }
61
62
0
        let target = TableStack {
63
0
            table: t,
64
0
            length: n,
65
0
            index: i,
66
0
            next: self.stack.take(),
67
0
        };
68
69
0
        self.stack = if let Some(mut s) = s {
70
0
            *s = target;
71
0
            Some(s)
72
        } else {
73
0
            Some(Box::new(target))
74
        };
75
0
    }
76
77
0
    fn recover_state(&mut self, mut n: usize) {
78
0
        while let Some(ref mut s) = self.stack {
79
0
            if self.index + s.length < n {
80
                // if we haven't checked the high "side" of this bucket,
81
                // then do _not_ pop the stack frame,
82
                // and instead moveon to that bin.
83
0
                self.index += s.length;
84
0
                break;
85
0
            }
86
0
87
0
            // we _are_ popping the stack
88
0
            let mut s = self.stack.take().expect("while let Some");
89
0
            n = s.length;
90
0
            self.index = s.index;
91
0
            self.table = Some(s.table);
92
0
            self.stack = s.next.take();
93
0
94
0
            // save stack frame for re-use
95
0
            s.next = self.spare.take();
96
0
            self.spare = Some(s);
97
        }
98
99
0
        if self.stack.is_none() {
100
            // move to next "part" of the top-level bin in the largest table
101
0
            self.index += self.base_size;
102
0
            if self.index >= n {
103
0
                // we've gone past the last part of this top-level bin,
104
0
                // so move to the _next_ top-level bin.
105
0
                self.base_index += 1;
106
0
                self.index = self.base_index;
107
0
            }
108
0
        }
109
0
    }
110
}
111
112
impl<'g, K, V> Iterator for NodeIter<'g, K, V> {
113
    type Item = &'g Node<K, V>;
114
0
    fn next(&mut self) -> Option<Self::Item> {
115
0
        let mut e = None;
116
0
        if let Some(prev) = self.prev {
117
0
            let next = prev.next.load(Ordering::SeqCst, self.guard);
118
0
            if !next.is_null() {
119
                // we have to check if we are iterating over a regular bin or a
120
                // TreeBin. the Java code gets away without this due to
121
                // inheritance (everything is a node), but we have to explicitly
122
                // check
123
                // safety: flurry does not drop or move until after guard drop
124
0
                match **unsafe { next.deref() } {
125
0
                    BinEntry::Node(ref node) => {
126
0
                        e = Some(node);
127
0
                    }
128
0
                    BinEntry::TreeNode(ref tree_node) => {
129
0
                        e = Some(&tree_node.node);
130
0
                    }
131
                    BinEntry::Moved | BinEntry::Tree(_) => {
132
0
                        unreachable!("Nodes can only point to Nodes or TreeNodes")
133
                    }
134
                }
135
0
            }
136
0
        }
137
138
        loop {
139
0
            if e.is_some() {
140
0
                self.prev = e;
141
0
                return e;
142
0
            }
143
0
144
0
            // safety: flurry does not drop or move until after guard drop
145
0
            if self.base_index >= self.base_limit
146
0
                || self.table.is_none()
147
0
                || self.table.as_ref().unwrap().len() <= self.index
148
            {
149
0
                self.prev = None;
150
0
                return None;
151
0
            }
152
0
153
0
            let t = self.table.expect("is_none in if above");
154
0
            let i = self.index;
155
0
            let n = t.len();
156
0
            let bin = t.bin(i, self.guard);
157
0
            if !bin.is_null() {
158
                // safety: flurry does not drop or move until after guard drop
159
0
                let bin = unsafe { bin.deref() };
160
0
                match **bin {
161
                    BinEntry::Moved => {
162
                        // recurse down into the target table
163
                        // safety: same argument as for following Moved in Table::find
164
0
                        self.table = Some(unsafe { t.next_table(self.guard).deref() });
165
0
                        self.prev = None;
166
0
                        // make sure we can get back "up" to where we're at
167
0
                        self.push_state(t, i, n);
168
0
                        continue;
169
                    }
170
0
                    BinEntry::Node(ref node) => {
171
0
                        e = Some(node);
172
0
                    }
173
0
                    BinEntry::Tree(ref tree_bin) => {
174
0
                        // since we want to iterate over all entries, TreeBins
175
0
                        // are also traversed via the `next` pointers of their
176
0
                        // contained node
177
0
                        e = Some(
178
0
                            // safety: `bin` was read under our guard, at which
179
0
                            // point the tree was valid. Since our guard marks
180
0
                            // the current thread as active, the TreeNodes remain valid for
181
0
                            // at least as long as we hold onto the guard.
182
0
                            // Structurally, TreeNodes always point to TreeNodes, so this is sound.
183
0
                            &unsafe {
184
0
                                TreeNode::get_tree_node(
185
0
                                    tree_bin.first.load(Ordering::SeqCst, self.guard),
186
0
                                )
187
0
                            }
188
0
                            .node,
189
0
                        );
190
0
                    }
191
0
                    BinEntry::TreeNode(_) => unreachable!(
192
0
                        "The head of a bin cannot be a TreeNode directly without BinEntry::Tree"
193
0
                    ),
194
                }
195
0
            }
196
197
0
            if self.stack.is_some() {
198
0
                self.recover_state(n);
199
0
            } else {
200
0
                self.index = i + self.base_size;
201
0
                if self.index >= n {
202
0
                    self.base_index += 1;
203
0
                    self.index = self.base_index;
204
0
                }
205
            }
206
        }
207
0
    }
208
}
209
210
#[derive(Debug)]
211
struct TableStack<'g, K, V> {
212
    length: usize,
213
    index: usize,
214
    table: &'g Linked<Table<K, V>>,
215
    next: Option<Box<TableStack<'g, K, V>>>,
216
}
217
218
#[cfg(test)]
219
mod tests {
220
    use super::*;
221
    use crate::raw::Table;
222
    use crate::reclaim::Atomic;
223
    use parking_lot::Mutex;
224
225
    #[test]
226
    fn iter_new() {
227
        let guard = unsafe { seize::Guard::unprotected() };
228
        let iter = NodeIter::<usize, usize>::new(Shared::null(), &guard);
229
        assert_eq!(iter.count(), 0);
230
    }
231
232
    #[test]
233
    fn iter_empty() {
234
        let collector = seize::Collector::new();
235
236
        let table = Shared::boxed(Table::<usize, usize>::new(16, &collector), &collector);
237
        let guard = collector.enter();
238
        let iter = NodeIter::new(table, &guard);
239
        assert_eq!(iter.count(), 0);
240
241
        // safety: nothing holds on to references into the table any more
242
        let mut t = unsafe { table.into_box() };
243
        t.drop_bins();
244
    }
245
246
    #[test]
247
    fn iter_simple() {
248
        let collector = seize::Collector::new();
249
        let mut bins = vec![Atomic::null(); 16];
250
        bins[8] = Atomic::from(Shared::boxed(
251
            BinEntry::Node(Node {
252
                hash: 0,
253
                key: 0usize,
254
                value: Atomic::from(Shared::boxed(0usize, &collector)),
255
                next: Atomic::null(),
256
                lock: Mutex::new(()),
257
            }),
258
            &collector,
259
        ));
260
261
        let table = Shared::boxed(Table::from(bins, &collector), &collector);
262
        let guard = collector.enter();
263
        {
264
            let mut iter = NodeIter::new(table, &guard);
265
            let e = iter.next().unwrap();
266
            assert_eq!(e.key, 0);
267
            assert!(iter.next().is_none());
268
        }
269
270
        // safety: nothing holds on to references into the table any more
271
        let mut t = unsafe { table.into_box() };
272
        t.drop_bins();
273
    }
274
275
    #[test]
276
    fn iter_fw() {
277
        // construct the forwarded-to table
278
        let collector = seize::Collector::new();
279
        let mut deep_bins = vec![Atomic::null(); 16];
280
        deep_bins[8] = Atomic::from(Shared::boxed(
281
            BinEntry::Node(Node {
282
                hash: 0,
283
                key: 0usize,
284
                value: Atomic::from(Shared::boxed(0usize, &collector)),
285
                next: Atomic::null(),
286
                lock: Mutex::new(()),
287
            }),
288
            &collector,
289
        ));
290
291
        let guard = collector.enter();
292
        let deep_table = Shared::boxed(Table::from(deep_bins, &collector), &collector);
293
294
        // construct the forwarded-from table
295
        let mut bins = vec![Shared::null(); 16];
296
        let table = Table::<usize, usize>::new(bins.len(), &collector);
297
        for bin in &mut bins[8..] {
298
            // this also sets table.next_table to deep_table
299
            *bin = table.get_moved(deep_table, &guard);
300
        }
301
        // this cannot use Table::from(bins), since we need the table to get
302
        // the Moved and set its next_table
303
        for i in 0..bins.len() {
304
            table.store_bin(i, bins[i]);
305
        }
306
        let table = Shared::boxed(table, &collector);
307
        {
308
            let mut iter = NodeIter::new(table, &guard);
309
            let e = iter.next().unwrap();
310
            assert_eq!(e.key, 0);
311
            assert!(iter.next().is_none());
312
        }
313
314
        // safety: nothing holds on to references into the table any more
315
        let mut t = unsafe { table.into_box() };
316
        t.drop_bins();
317
        // no one besides this test case uses deep_table
318
        unsafe { deep_table.into_box() }.drop_bins();
319
    }
320
}