/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 | | } |