crossbeam-epoch/src/collector.rs
Line | Count | Source (jump to first uncovered line) |
1 | | /// Epoch-based garbage collector. |
2 | | /// |
3 | | /// # Examples |
4 | | /// |
5 | | /// ``` |
6 | | /// use crossbeam_epoch::Collector; |
7 | | /// |
8 | | /// let collector = Collector::new(); |
9 | | /// |
10 | | /// let handle = collector.register(); |
11 | | /// drop(collector); // `handle` still works after dropping `collector` |
12 | | /// |
13 | | /// handle.pin().flush(); |
14 | | /// ``` |
15 | | use core::fmt; |
16 | | |
17 | | use crate::primitive::sync::Arc; |
18 | | use crate::guard::Guard; |
19 | | use crate::internal::{Global, Local}; |
20 | | |
21 | | /// An epoch-based garbage collector. |
22 | | pub struct Collector { |
23 | | pub(crate) global: Arc<Global>, |
24 | | } |
25 | | |
26 | | unsafe impl Send for Collector {} |
27 | | unsafe impl Sync for Collector {} |
28 | | |
29 | | impl Default for Collector { |
30 | 59 | fn default() -> Self { |
31 | 59 | Self { |
32 | 59 | global: Arc::new(Global::new()), |
33 | 59 | } |
34 | 59 | } |
35 | | } |
36 | | |
37 | | impl Collector { |
38 | | /// Creates a new collector. |
39 | 59 | pub fn new() -> Self { |
40 | 59 | Self::default() |
41 | 59 | } <crossbeam_epoch::collector::Collector>::new Line | Count | Source | 39 | 43 | pub fn new() -> Self { | 40 | 43 | Self::default() | 41 | 43 | } |
<crossbeam_epoch::collector::Collector>::new Line | Count | Source | 39 | 16 | pub fn new() -> Self { | 40 | 16 | Self::default() | 41 | 16 | } |
|
42 | | |
43 | | /// Registers a new handle for the collector. |
44 | 214 | pub fn register(&self) -> LocalHandle { |
45 | 214 | Local::register(self) |
46 | 214 | } <crossbeam_epoch::collector::Collector>::register Line | Count | Source | 44 | 146 | pub fn register(&self) -> LocalHandle { | 45 | 146 | Local::register(self) | 46 | 146 | } |
<crossbeam_epoch::collector::Collector>::register Line | Count | Source | 44 | 68 | pub fn register(&self) -> LocalHandle { | 45 | 68 | Local::register(self) | 46 | 68 | } |
|
47 | | } |
48 | | |
49 | | impl Clone for Collector { |
50 | | /// Creates another reference to the same garbage collector. |
51 | 238 | fn clone(&self) -> Self { |
52 | 238 | Collector { |
53 | 238 | global: self.global.clone(), |
54 | 238 | } |
55 | 238 | } <crossbeam_epoch::collector::Collector as core::clone::Clone>::clone Line | Count | Source | 51 | 170 | fn clone(&self) -> Self { | 52 | 170 | Collector { | 53 | 170 | global: self.global.clone(), | 54 | 170 | } | 55 | 170 | } |
<crossbeam_epoch::collector::Collector as core::clone::Clone>::clone Line | Count | Source | 51 | 68 | fn clone(&self) -> Self { | 52 | 68 | Collector { | 53 | 68 | global: self.global.clone(), | 54 | 68 | } | 55 | 68 | } |
|
56 | | } |
57 | | |
58 | | impl fmt::Debug for Collector { |
59 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
60 | 0 | f.pad("Collector { .. }") |
61 | 0 | } |
62 | | } |
63 | | |
64 | | impl PartialEq for Collector { |
65 | | /// Checks if both handles point to the same collector. |
66 | 693 | fn eq(&self, rhs: &Collector) -> bool { |
67 | 693 | Arc::ptr_eq(&self.global, &rhs.global) |
68 | 693 | } |
69 | | } |
70 | | impl Eq for Collector {} |
71 | | |
72 | | /// A handle to a garbage collector. |
73 | | pub struct LocalHandle { |
74 | | pub(crate) local: *const Local, |
75 | | } |
76 | | |
77 | | impl LocalHandle { |
78 | | /// Pins the handle. |
79 | | #[inline] |
80 | 13.4M | pub fn pin(&self) -> Guard { |
81 | 13.4M | unsafe { (*self.local).pin() } |
82 | 13.4M | } <crossbeam_epoch::collector::LocalHandle>::pin Line | Count | Source | 80 | 206 | pub fn pin(&self) -> Guard { | 81 | 206 | unsafe { (*self.local).pin() } | 82 | 206 | } |
<crossbeam_epoch::collector::LocalHandle>::pin Line | Count | Source | 80 | 3 | pub fn pin(&self) -> Guard { | 81 | 3 | unsafe { (*self.local).pin() } | 82 | 3 | } |
<crossbeam_epoch::collector::LocalHandle>::pin Line | Count | Source | 80 | 21 | pub fn pin(&self) -> Guard { | 81 | 21 | unsafe { (*self.local).pin() } | 82 | 21 | } |
<crossbeam_epoch::collector::LocalHandle>::pin Line | Count | Source | 80 | 1 | pub fn pin(&self) -> Guard { | 81 | 1 | unsafe { (*self.local).pin() } | 82 | 1 | } |
<crossbeam_epoch::collector::LocalHandle>::pin Line | Count | Source | 80 | 427k | pub fn pin(&self) -> Guard { | 81 | 427k | unsafe { (*self.local).pin() } | 82 | 427k | } |
<crossbeam_epoch::collector::LocalHandle>::pin Line | Count | Source | 80 | 14 | pub fn pin(&self) -> Guard { | 81 | 14 | unsafe { (*self.local).pin() } | 82 | 14 | } |
<crossbeam_epoch::collector::LocalHandle>::pin Line | Count | Source | 80 | 559k | pub fn pin(&self) -> Guard { | 81 | 559k | unsafe { (*self.local).pin() } | 82 | 559k | } |
<crossbeam_epoch::collector::LocalHandle>::pin Line | Count | Source | 80 | 12.4M | pub fn pin(&self) -> Guard { | 81 | 12.4M | unsafe { (*self.local).pin() } | 82 | 12.4M | } |
|
83 | | |
84 | | /// Returns `true` if the handle is pinned. |
85 | | #[inline] |
86 | 897k | pub fn is_pinned(&self) -> bool { |
87 | 897k | unsafe { (*self.local).is_pinned() } |
88 | 897k | } <crossbeam_epoch::collector::LocalHandle>::is_pinned Line | Count | Source | 86 | 389k | pub fn is_pinned(&self) -> bool { | 87 | 389k | unsafe { (*self.local).is_pinned() } | 88 | 389k | } |
<crossbeam_epoch::collector::LocalHandle>::is_pinned Line | Count | Source | 86 | 14 | pub fn is_pinned(&self) -> bool { | 87 | 14 | unsafe { (*self.local).is_pinned() } | 88 | 14 | } |
<crossbeam_epoch::collector::LocalHandle>::is_pinned Line | Count | Source | 86 | 507k | pub fn is_pinned(&self) -> bool { | 87 | 507k | unsafe { (*self.local).is_pinned() } | 88 | 507k | } |
<crossbeam_epoch::collector::LocalHandle>::is_pinned Line | Count | Source | 86 | 5 | pub fn is_pinned(&self) -> bool { | 87 | 5 | unsafe { (*self.local).is_pinned() } | 88 | 5 | } |
|
89 | | |
90 | | /// Returns the `Collector` associated with this handle. |
91 | | #[inline] |
92 | 0 | pub fn collector(&self) -> &Collector { |
93 | 0 | unsafe { (*self.local).collector() } |
94 | 0 | } <crossbeam_epoch::collector::Collector as core::default::Default>::default Line | Count | Source | 30 | 43 | fn default() -> Self { | 31 | 43 | Self { | 32 | 43 | global: Arc::new(Global::new()), | 33 | 43 | } | 34 | 43 | } | 35 | | } | 36 | | | 37 | | impl Collector { | 38 | | /// Creates a new collector. | 39 | | pub fn new() -> Self { | 40 | | Self::default() | 41 | | } | 42 | | | 43 | | /// Registers a new handle for the collector. | 44 | | pub fn register(&self) -> LocalHandle { | 45 | | Local::register(self) | 46 | | } | 47 | | } | 48 | | | 49 | | impl Clone for Collector { | 50 | | /// Creates another reference to the same garbage collector. | 51 | | fn clone(&self) -> Self { | 52 | | Collector { | 53 | | global: self.global.clone(), | 54 | | } | 55 | | } | 56 | | } | 57 | | | 58 | | impl fmt::Debug for Collector { | 59 | | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | 60 | | f.pad("Collector { .. }") | 61 | | } | 62 | | } | 63 | | | 64 | | impl PartialEq for Collector { | 65 | | /// Checks if both handles point to the same collector. | 66 | | fn eq(&self, rhs: &Collector) -> bool { | 67 | | Arc::ptr_eq(&self.global, &rhs.global) | 68 | | } | 69 | | } | 70 | | impl Eq for Collector {} | 71 | | | 72 | | /// A handle to a garbage collector. | 73 | | pub struct LocalHandle { | 74 | | pub(crate) local: *const Local, | 75 | | } | 76 | | | 77 | | impl LocalHandle { | 78 | | /// Pins the handle. | 79 | | #[inline] | 80 | 0 | pub fn pin(&self) -> Guard { | 81 | 0 | unsafe { (*self.local).pin() } | 82 | 0 | } | 83 | | | 84 | | /// Returns `true` if the handle is pinned. | 85 | | #[inline] | 86 | 0 | pub fn is_pinned(&self) -> bool { | 87 | 0 | unsafe { (*self.local).is_pinned() } | 88 | 0 | } | 89 | | | 90 | | /// Returns the `Collector` associated with this handle. | 91 | | #[inline] | 92 | 0 | pub fn collector(&self) -> &Collector { | 93 | 0 | unsafe { (*self.local).collector() } | 94 | 0 | } |
|
95 | | } |
96 | | |
97 | | impl Drop for LocalHandle { |
98 | | #[inline] |
99 | 215 | fn drop(&mut self) { |
100 | 215 | unsafe { |
101 | 215 | Local::release_handle(&*self.local); |
102 | 215 | } |
103 | 215 | } <crossbeam_epoch::collector::LocalHandle as core::ops::drop::Drop>::drop Line | Count | Source | 99 | 145 | fn drop(&mut self) { | 100 | 145 | unsafe { | 101 | 145 | Local::release_handle(&*self.local); | 102 | 145 | } | 103 | 145 | } |
<crossbeam_epoch::collector::LocalHandle as core::ops::drop::Drop>::drop Line | Count | Source | 99 | 70 | fn drop(&mut self) { | 100 | 70 | unsafe { | 101 | 70 | Local::release_handle(&*self.local); | 102 | 70 | } | 103 | 70 | } |
|
104 | | } |
105 | | |
106 | | impl fmt::Debug for LocalHandle { |
107 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
108 | 0 | f.pad("LocalHandle { .. }") |
109 | 0 | } <crossbeam_epoch::collector::Collector as core::default::Default>::default Line | Count | Source | 30 | 16 | fn default() -> Self { | 31 | 16 | Self { | 32 | 16 | global: Arc::new(Global::new()), | 33 | 16 | } | 34 | 16 | } | 35 | | } | 36 | | | 37 | | impl Collector { | 38 | | /// Creates a new collector. | 39 | | pub fn new() -> Self { | 40 | | Self::default() | 41 | | } | 42 | | | 43 | | /// Registers a new handle for the collector. | 44 | | pub fn register(&self) -> LocalHandle { | 45 | | Local::register(self) | 46 | | } | 47 | | } | 48 | | | 49 | | impl Clone for Collector { | 50 | | /// Creates another reference to the same garbage collector. | 51 | | fn clone(&self) -> Self { | 52 | | Collector { | 53 | | global: self.global.clone(), | 54 | | } | 55 | | } | 56 | | } | 57 | | | 58 | | impl fmt::Debug for Collector { | 59 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | 60 | 0 | f.pad("Collector { .. }") | 61 | 0 | } | 62 | | } | 63 | | | 64 | | impl PartialEq for Collector { | 65 | | /// Checks if both handles point to the same collector. | 66 | 0 | fn eq(&self, rhs: &Collector) -> bool { | 67 | 0 | Arc::ptr_eq(&self.global, &rhs.global) | 68 | 0 | } | 69 | | } | 70 | | impl Eq for Collector {} | 71 | | | 72 | | /// A handle to a garbage collector. | 73 | | pub struct LocalHandle { | 74 | | pub(crate) local: *const Local, | 75 | | } | 76 | | | 77 | | impl LocalHandle { | 78 | | /// Pins the handle. | 79 | | #[inline] | 80 | | pub fn pin(&self) -> Guard { | 81 | | unsafe { (*self.local).pin() } | 82 | | } | 83 | | | 84 | | /// Returns `true` if the handle is pinned. | 85 | | #[inline] | 86 | | pub fn is_pinned(&self) -> bool { | 87 | | unsafe { (*self.local).is_pinned() } | 88 | | } | 89 | | | 90 | | /// Returns the `Collector` associated with this handle. | 91 | | #[inline] | 92 | 0 | pub fn collector(&self) -> &Collector { | 93 | 0 | unsafe { (*self.local).collector() } | 94 | 0 | } | 95 | | } | 96 | | | 97 | | impl Drop for LocalHandle { | 98 | | #[inline] | 99 | | fn drop(&mut self) { | 100 | | unsafe { | 101 | | Local::release_handle(&*self.local); | 102 | | } | 103 | | } | 104 | | } | 105 | | | 106 | | impl fmt::Debug for LocalHandle { | 107 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | 108 | 0 | f.pad("LocalHandle { .. }") | 109 | 0 | } |
|
110 | | } |
111 | | |
112 | | #[cfg(all(test, not(loom_crossbeam)))] |
113 | | mod tests { |
114 | | use std::mem; |
115 | | use std::sync::atomic::{AtomicUsize, Ordering}; |
116 | | |
117 | | use crossbeam_utils::thread; |
118 | | |
119 | | use crate::{Collector, Owned}; |
120 | | |
121 | | const NUM_THREADS: usize = 8; |
122 | | |
123 | | #[test] |
124 | 1 | fn pin_reentrant() { crossbeam_epoch::collector::tests::pin_reentrant::{closure#0} Line | Count | Source | 124 | 1 | fn pin_reentrant() { |
|
125 | 1 | let collector = Collector::new(); |
126 | 1 | let handle = collector.register(); |
127 | 1 | drop(collector); |
128 | | |
129 | 1 | assert!(!handle.is_pinned()); |
130 | | { |
131 | 1 | let _guard = &handle.pin(); |
132 | 1 | assert!(handle.is_pinned()); |
133 | | { |
134 | 1 | let _guard = &handle.pin(); |
135 | 1 | assert!(handle.is_pinned()); |
136 | | } |
137 | 1 | assert!(handle.is_pinned()); |
138 | | } |
139 | 1 | assert!(!handle.is_pinned()); |
140 | 1 | } crossbeam_epoch::collector::tests::pin_reentrant Line | Count | Source | 124 | 1 | fn pin_reentrant() { | 125 | 1 | let collector = Collector::new(); | 126 | 1 | let handle = collector.register(); | 127 | 1 | drop(collector); | 128 | | | 129 | 1 | assert!(!handle.is_pinned()); | 130 | | { | 131 | 1 | let _guard = &handle.pin(); | 132 | 1 | assert!(handle.is_pinned()); | 133 | | { | 134 | 1 | let _guard = &handle.pin(); | 135 | 1 | assert!(handle.is_pinned()); | 136 | | } | 137 | 1 | assert!(handle.is_pinned()); | 138 | | } | 139 | 1 | assert!(!handle.is_pinned()); | 140 | 1 | } |
|
141 | | |
142 | | #[test] |
143 | 1 | fn flush_local_bag() { crossbeam_epoch::collector::tests::flush_local_bag::{closure#0} Line | Count | Source | 143 | 1 | fn flush_local_bag() { |
|
144 | 1 | let collector = Collector::new(); |
145 | 1 | let handle = collector.register(); |
146 | 1 | drop(collector); |
147 | | |
148 | 101 | for _100 in 0..100 { |
149 | 100 | let guard = &handle.pin(); |
150 | 100 | unsafe { |
151 | 100 | let a = Owned::new(7).into_shared(guard); |
152 | 100 | guard.defer_destroy(a); |
153 | | |
154 | 100 | assert!(!(*guard.local).bag.with(|b| (*b).is_empty())); |
155 | | |
156 | 298 | while !(*guard.local).bag.with(|b| (*b).is_empty()) { |
157 | 198 | guard.flush(); |
158 | 198 | } |
159 | | } |
160 | | } |
161 | 1 | } crossbeam_epoch::collector::tests::flush_local_bag Line | Count | Source | 143 | 1 | fn flush_local_bag() { | 144 | 1 | let collector = Collector::new(); | 145 | 1 | let handle = collector.register(); | 146 | 1 | drop(collector); | 147 | | | 148 | 101 | for _100 in 0..100 { | 149 | 100 | let guard = &handle.pin(); | 150 | 100 | unsafe { | 151 | 100 | let a = Owned::new(7).into_shared(guard); | 152 | 100 | guard.defer_destroy(a); | 153 | | | 154 | 100 | assert!(!(*guard.local).bag.with(|b| (*b).is_empty())); | 155 | | | 156 | 298 | while !(*guard.local).bag.with(|b| (*b).is_empty()) { | 157 | 198 | guard.flush(); | 158 | 198 | } | 159 | | } | 160 | | } | 161 | 1 | } |
|
162 | | |
163 | | #[test] |
164 | 1 | fn garbage_buffering() { crossbeam_epoch::collector::tests::garbage_buffering::{closure#0} Line | Count | Source | 164 | 1 | fn garbage_buffering() { |
|
165 | 1 | let collector = Collector::new(); |
166 | 1 | let handle = collector.register(); |
167 | 1 | drop(collector); |
168 | 1 | |
169 | 1 | let guard = &handle.pin(); |
170 | | unsafe { |
171 | 11 | for _10 in 0..10 { |
172 | 10 | let a = Owned::new(7).into_shared(guard); |
173 | 10 | guard.defer_destroy(a); |
174 | 10 | } |
175 | 1 | assert!(!(*guard.local).bag.with(|b| (*b).is_empty())); |
176 | | } |
177 | 1 | } crossbeam_epoch::collector::tests::garbage_buffering Line | Count | Source | 164 | 1 | fn garbage_buffering() { | 165 | 1 | let collector = Collector::new(); | 166 | 1 | let handle = collector.register(); | 167 | 1 | drop(collector); | 168 | 1 | | 169 | 1 | let guard = &handle.pin(); | 170 | | unsafe { | 171 | 11 | for _10 in 0..10 { | 172 | 10 | let a = Owned::new(7).into_shared(guard); | 173 | 10 | guard.defer_destroy(a); | 174 | 10 | } | 175 | 1 | assert!(!(*guard.local).bag.with(|b| (*b).is_empty())); | 176 | | } | 177 | 1 | } |
|
178 | | |
179 | | #[test] |
180 | 1 | fn pin_holds_advance() { crossbeam_epoch::collector::tests::pin_holds_advance::{closure#0} Line | Count | Source | 180 | 1 | fn pin_holds_advance() { |
|
181 | 1 | let collector = Collector::new(); |
182 | 1 | |
183 | 1 | thread::scope(|scope| { |
184 | 9 | for _8 in 0..NUM_THREADS { |
185 | 8 | scope.spawn(|_| { |
186 | 8 | let handle = collector.register(); |
187 | 3.50M | for _3.50M in 0..500_000 { |
188 | 3.50M | let guard = &handle.pin(); |
189 | 3.50M | |
190 | 3.50M | let before = collector.global.epoch.load(Ordering::Relaxed); |
191 | 3.50M | collector.global.collect(guard); |
192 | 3.50M | let after = collector.global.epoch.load(Ordering::Relaxed); |
193 | | |
194 | 3.50M | assert!(after.wrapping_sub(before) <= 2); |
195 | | } |
196 | 8 | }); |
197 | 8 | } |
198 | 1 | }) |
199 | 1 | .unwrap(); |
200 | 1 | } crossbeam_epoch::collector::tests::pin_holds_advance Line | Count | Source | 180 | 1 | fn pin_holds_advance() { | 181 | 1 | let collector = Collector::new(); | 182 | 1 | | 183 | 1 | thread::scope(|scope| { | 184 | | for _ in 0..NUM_THREADS { | 185 | | scope.spawn(|_| { | 186 | | let handle = collector.register(); | 187 | | for _ in 0..500_000 { | 188 | | let guard = &handle.pin(); | 189 | | | 190 | | let before = collector.global.epoch.load(Ordering::Relaxed); | 191 | | collector.global.collect(guard); | 192 | | let after = collector.global.epoch.load(Ordering::Relaxed); | 193 | | | 194 | | assert!(after.wrapping_sub(before) <= 2); | 195 | | } | 196 | | }); | 197 | | } | 198 | 1 | }) | 199 | 1 | .unwrap(); | 200 | 1 | } |
|
201 | | |
202 | | #[test] |
203 | 1 | fn incremental() { crossbeam_epoch::collector::tests::incremental::{closure#0} Line | Count | Source | 203 | 1 | fn incremental() { |
|
204 | | const COUNT: usize = 100_000; |
205 | | static DESTROYS: AtomicUsize = AtomicUsize::new(0); |
206 | | |
207 | 1 | let collector = Collector::new(); |
208 | 1 | let handle = collector.register(); |
209 | | |
210 | | unsafe { |
211 | 1 | let guard = &handle.pin(); |
212 | 100k | for _100k in 0..COUNT { |
213 | 100k | let a = Owned::new(7i32).into_shared(guard); |
214 | 100k | guard.defer_unchecked(move || { |
215 | 100k | drop(a.into_owned()); |
216 | 100k | DESTROYS.fetch_add(1, Ordering::Relaxed); |
217 | 100k | }); |
218 | 100k | } |
219 | 1 | guard.flush(); |
220 | 1 | } |
221 | 1 | |
222 | 1 | let mut last = 0; |
223 | | |
224 | 204 | while last < COUNT { |
225 | 203 | let curr = DESTROYS.load(Ordering::Relaxed); |
226 | 203 | assert!(curr - last <= 1024); |
227 | 203 | last = curr; |
228 | 203 | |
229 | 203 | let guard = &handle.pin(); |
230 | 203 | collector.global.collect(guard); |
231 | | } |
232 | 1 | assert!(DESTROYS.load(Ordering::Relaxed) == 100_000); |
233 | 1 | } crossbeam_epoch::collector::tests::incremental Line | Count | Source | 203 | 1 | fn incremental() { | 204 | | const COUNT: usize = 100_000; | 205 | | static DESTROYS: AtomicUsize = AtomicUsize::new(0); | 206 | | | 207 | 1 | let collector = Collector::new(); | 208 | 1 | let handle = collector.register(); | 209 | | | 210 | | unsafe { | 211 | 1 | let guard = &handle.pin(); | 212 | 100k | for _100k in 0..COUNT { | 213 | 100k | let a = Owned::new(7i32).into_shared(guard); | 214 | 100k | guard.defer_unchecked(move || { | 215 | | drop(a.into_owned()); | 216 | | DESTROYS.fetch_add(1, Ordering::Relaxed); | 217 | 100k | }); | 218 | 100k | } | 219 | 1 | guard.flush(); | 220 | 1 | } | 221 | 1 | | 222 | 1 | let mut last = 0; | 223 | | | 224 | 204 | while last < COUNT { | 225 | 203 | let curr = DESTROYS.load(Ordering::Relaxed); | 226 | 203 | assert!(curr - last <= 1024); | 227 | 203 | last = curr; | 228 | 203 | | 229 | 203 | let guard = &handle.pin(); | 230 | 203 | collector.global.collect(guard); | 231 | | } | 232 | 1 | assert!(DESTROYS.load(Ordering::Relaxed) == 100_000); | 233 | 1 | } |
|
234 | | |
235 | | #[test] |
236 | 1 | fn buffering() { crossbeam_epoch::collector::tests::buffering::{closure#0} Line | Count | Source | 236 | 1 | fn buffering() { |
|
237 | | const COUNT: usize = 10; |
238 | | static DESTROYS: AtomicUsize = AtomicUsize::new(0); |
239 | | |
240 | 1 | let collector = Collector::new(); |
241 | 1 | let handle = collector.register(); |
242 | 1 | |
243 | 1 | unsafe { |
244 | 1 | let guard = &handle.pin(); |
245 | 11 | for _10 in 0..COUNT { |
246 | 10 | let a = Owned::new(7i32).into_shared(guard); |
247 | 10 | guard.defer_unchecked(move || { |
248 | 10 | drop(a.into_owned()); |
249 | 10 | DESTROYS.fetch_add(1, Ordering::Relaxed); |
250 | 10 | }); |
251 | 10 | } |
252 | | } |
253 | | |
254 | 100k | for _100k in 0..100_000 { |
255 | 100k | collector.global.collect(&handle.pin()); |
256 | 100k | } |
257 | 1 | assert!(DESTROYS.load(Ordering::Relaxed) < COUNT); |
258 | | |
259 | 1 | handle.pin().flush(); |
260 | | |
261 | 2 | while DESTROYS.load(Ordering::Relaxed) < COUNT { |
262 | 1 | let guard = &handle.pin(); |
263 | 1 | collector.global.collect(guard); |
264 | 1 | } |
265 | 1 | assert_eq!(DESTROYS.load(Ordering::Relaxed), COUNT); |
266 | 1 | } crossbeam_epoch::collector::tests::buffering Line | Count | Source | 236 | 1 | fn buffering() { | 237 | | const COUNT: usize = 10; | 238 | | static DESTROYS: AtomicUsize = AtomicUsize::new(0); | 239 | | | 240 | 1 | let collector = Collector::new(); | 241 | 1 | let handle = collector.register(); | 242 | 1 | | 243 | 1 | unsafe { | 244 | 1 | let guard = &handle.pin(); | 245 | 11 | for _10 in 0..COUNT { | 246 | 10 | let a = Owned::new(7i32).into_shared(guard); | 247 | 10 | guard.defer_unchecked(move || { | 248 | | drop(a.into_owned()); | 249 | | DESTROYS.fetch_add(1, Ordering::Relaxed); | 250 | 10 | }); | 251 | 10 | } | 252 | | } | 253 | | | 254 | 100k | for _100k in 0..100_000 { | 255 | 100k | collector.global.collect(&handle.pin()); | 256 | 100k | } | 257 | 1 | assert!(DESTROYS.load(Ordering::Relaxed) < COUNT); | 258 | | | 259 | 1 | handle.pin().flush(); | 260 | | | 261 | 2 | while DESTROYS.load(Ordering::Relaxed) < COUNT { | 262 | 1 | let guard = &handle.pin(); | 263 | 1 | collector.global.collect(guard); | 264 | 1 | } | 265 | 1 | assert_eq!(DESTROYS.load(Ordering::Relaxed), COUNT); | 266 | 1 | } |
|
267 | | |
268 | | #[test] |
269 | 1 | fn count_drops() { crossbeam_epoch::collector::tests::count_drops::{closure#0} Line | Count | Source | 269 | 1 | fn count_drops() { |
|
270 | | const COUNT: usize = 100_000; |
271 | | static DROPS: AtomicUsize = AtomicUsize::new(0); |
272 | | |
273 | | struct Elem(i32); |
274 | | |
275 | | impl Drop for Elem { |
276 | 100k | fn drop(&mut self) { |
277 | 100k | DROPS.fetch_add(1, Ordering::Relaxed); |
278 | 100k | } |
279 | | } |
280 | | |
281 | 1 | let collector = Collector::new(); |
282 | 1 | let handle = collector.register(); |
283 | | |
284 | | unsafe { |
285 | 1 | let guard = &handle.pin(); |
286 | | |
287 | 100k | for _100k in 0..COUNT { |
288 | 100k | let a = Owned::new(Elem(7i32)).into_shared(guard); |
289 | 100k | guard.defer_destroy(a); |
290 | 100k | } |
291 | 1 | guard.flush(); |
292 | | } |
293 | | |
294 | 203 | while DROPS.load(Ordering::Relaxed) < COUNT { |
295 | 202 | let guard = &handle.pin(); |
296 | 202 | collector.global.collect(guard); |
297 | 202 | } |
298 | 1 | assert_eq!(DROPS.load(Ordering::Relaxed), COUNT); |
299 | 1 | } crossbeam_epoch::collector::tests::count_drops Line | Count | Source | 269 | 1 | fn count_drops() { | 270 | | const COUNT: usize = 100_000; | 271 | | static DROPS: AtomicUsize = AtomicUsize::new(0); | 272 | | | 273 | | struct Elem(i32); | 274 | | | 275 | | impl Drop for Elem { | 276 | | fn drop(&mut self) { | 277 | | DROPS.fetch_add(1, Ordering::Relaxed); | 278 | | } | 279 | | } | 280 | | | 281 | 1 | let collector = Collector::new(); | 282 | 1 | let handle = collector.register(); | 283 | | | 284 | | unsafe { | 285 | 1 | let guard = &handle.pin(); | 286 | | | 287 | 100k | for _100k in 0..COUNT { | 288 | 100k | let a = Owned::new(Elem(7i32)).into_shared(guard); | 289 | 100k | guard.defer_destroy(a); | 290 | 100k | } | 291 | 1 | guard.flush(); | 292 | | } | 293 | | | 294 | 203 | while DROPS.load(Ordering::Relaxed) < COUNT { | 295 | 202 | let guard = &handle.pin(); | 296 | 202 | collector.global.collect(guard); | 297 | 202 | } | 298 | 1 | assert_eq!(DROPS.load(Ordering::Relaxed), COUNT); | 299 | 1 | } |
|
300 | | |
301 | | #[test] |
302 | 1 | fn count_destroy() { crossbeam_epoch::collector::tests::count_destroy::{closure#0} Line | Count | Source | 302 | 1 | fn count_destroy() { |
|
303 | | const COUNT: usize = 100_000; |
304 | | static DESTROYS: AtomicUsize = AtomicUsize::new(0); |
305 | | |
306 | 1 | let collector = Collector::new(); |
307 | 1 | let handle = collector.register(); |
308 | | |
309 | | unsafe { |
310 | 1 | let guard = &handle.pin(); |
311 | | |
312 | 100k | for _100k in 0..COUNT { |
313 | 100k | let a = Owned::new(7i32).into_shared(guard); |
314 | 100k | guard.defer_unchecked(move || { |
315 | 100k | drop(a.into_owned()); |
316 | 100k | DESTROYS.fetch_add(1, Ordering::Relaxed); |
317 | 100k | }); |
318 | 100k | } |
319 | 1 | guard.flush(); |
320 | | } |
321 | | |
322 | 203 | while DESTROYS.load(Ordering::Relaxed) < COUNT { |
323 | 202 | let guard = &handle.pin(); |
324 | 202 | collector.global.collect(guard); |
325 | 202 | } |
326 | 1 | assert_eq!(DESTROYS.load(Ordering::Relaxed), COUNT); |
327 | 1 | } crossbeam_epoch::collector::tests::count_destroy Line | Count | Source | 302 | 1 | fn count_destroy() { | 303 | | const COUNT: usize = 100_000; | 304 | | static DESTROYS: AtomicUsize = AtomicUsize::new(0); | 305 | | | 306 | 1 | let collector = Collector::new(); | 307 | 1 | let handle = collector.register(); | 308 | | | 309 | | unsafe { | 310 | 1 | let guard = &handle.pin(); | 311 | | | 312 | 100k | for _100k in 0..COUNT { | 313 | 100k | let a = Owned::new(7i32).into_shared(guard); | 314 | 100k | guard.defer_unchecked(move || { | 315 | | drop(a.into_owned()); | 316 | | DESTROYS.fetch_add(1, Ordering::Relaxed); | 317 | 100k | }); | 318 | 100k | } | 319 | 1 | guard.flush(); | 320 | | } | 321 | | | 322 | 203 | while DESTROYS.load(Ordering::Relaxed) < COUNT { | 323 | 202 | let guard = &handle.pin(); | 324 | 202 | collector.global.collect(guard); | 325 | 202 | } | 326 | 1 | assert_eq!(DESTROYS.load(Ordering::Relaxed), COUNT); | 327 | 1 | } |
|
328 | | |
329 | | #[test] |
330 | 1 | fn drop_array() { crossbeam_epoch::collector::tests::drop_array::{closure#0} Line | Count | Source | 330 | 1 | fn drop_array() { |
|
331 | | const COUNT: usize = 700; |
332 | | static DROPS: AtomicUsize = AtomicUsize::new(0); |
333 | | |
334 | | struct Elem(i32); |
335 | | |
336 | | impl Drop for Elem { |
337 | 700 | fn drop(&mut self) { |
338 | 700 | DROPS.fetch_add(1, Ordering::Relaxed); |
339 | 700 | } |
340 | | } |
341 | | |
342 | 1 | let collector = Collector::new(); |
343 | 1 | let handle = collector.register(); |
344 | 1 | |
345 | 1 | let mut guard = handle.pin(); |
346 | 1 | |
347 | 1 | let mut v = Vec::with_capacity(COUNT); |
348 | 700 | for i in 0..COUNT { |
349 | 700 | v.push(Elem(i as i32)); |
350 | 700 | } |
351 | | |
352 | 1 | { |
353 | 1 | let a = Owned::new(v).into_shared(&guard); |
354 | 1 | unsafe { |
355 | 1 | guard.defer_destroy(a); |
356 | 1 | } |
357 | 1 | guard.flush(); |
358 | 1 | } |
359 | | |
360 | 3 | while DROPS.load(Ordering::Relaxed) < COUNT { |
361 | 2 | guard.repin(); |
362 | 2 | collector.global.collect(&guard); |
363 | 2 | } |
364 | 1 | assert_eq!(DROPS.load(Ordering::Relaxed), COUNT); |
365 | 1 | } crossbeam_epoch::collector::tests::drop_array Line | Count | Source | 330 | 1 | fn drop_array() { | 331 | | const COUNT: usize = 700; | 332 | | static DROPS: AtomicUsize = AtomicUsize::new(0); | 333 | | | 334 | | struct Elem(i32); | 335 | | | 336 | | impl Drop for Elem { | 337 | | fn drop(&mut self) { | 338 | | DROPS.fetch_add(1, Ordering::Relaxed); | 339 | | } | 340 | | } | 341 | | | 342 | 1 | let collector = Collector::new(); | 343 | 1 | let handle = collector.register(); | 344 | 1 | | 345 | 1 | let mut guard = handle.pin(); | 346 | 1 | | 347 | 1 | let mut v = Vec::with_capacity(COUNT); | 348 | 700 | for i in 0..COUNT { | 349 | 700 | v.push(Elem(i as i32)); | 350 | 700 | } | 351 | | | 352 | 1 | { | 353 | 1 | let a = Owned::new(v).into_shared(&guard); | 354 | 1 | unsafe { | 355 | 1 | guard.defer_destroy(a); | 356 | 1 | } | 357 | 1 | guard.flush(); | 358 | 1 | } | 359 | | | 360 | 3 | while DROPS.load(Ordering::Relaxed) < COUNT { | 361 | 2 | guard.repin(); | 362 | 2 | collector.global.collect(&guard); | 363 | 2 | } | 364 | 1 | assert_eq!(DROPS.load(Ordering::Relaxed), COUNT); | 365 | 1 | } |
|
366 | | |
367 | | #[test] |
368 | 1 | fn destroy_array() { crossbeam_epoch::collector::tests::destroy_array::{closure#0} Line | Count | Source | 368 | 1 | fn destroy_array() { |
|
369 | | const COUNT: usize = 100_000; |
370 | | static DESTROYS: AtomicUsize = AtomicUsize::new(0); |
371 | | |
372 | 1 | let collector = Collector::new(); |
373 | 1 | let handle = collector.register(); |
374 | | |
375 | | unsafe { |
376 | 1 | let guard = &handle.pin(); |
377 | 1 | |
378 | 1 | let mut v = Vec::with_capacity(COUNT); |
379 | 100k | for i in 0..COUNT { |
380 | 100k | v.push(i as i32); |
381 | 100k | } |
382 | | |
383 | 1 | let ptr = v.as_mut_ptr() as usize; |
384 | 1 | let len = v.len(); |
385 | 1 | guard.defer_unchecked(move || { |
386 | 1 | drop(Vec::from_raw_parts(ptr as *const i32 as *mut i32, len, len)); |
387 | 1 | DESTROYS.fetch_add(len, Ordering::Relaxed); |
388 | 1 | }); |
389 | 1 | guard.flush(); |
390 | 1 | |
391 | 1 | mem::forget(v); |
392 | | } |
393 | | |
394 | 3 | while DESTROYS.load(Ordering::Relaxed) < COUNT { |
395 | 2 | let guard = &handle.pin(); |
396 | 2 | collector.global.collect(guard); |
397 | 2 | } |
398 | 1 | assert_eq!(DESTROYS.load(Ordering::Relaxed), COUNT); |
399 | 1 | } crossbeam_epoch::collector::tests::destroy_array Line | Count | Source | 368 | 1 | fn destroy_array() { | 369 | | const COUNT: usize = 100_000; | 370 | | static DESTROYS: AtomicUsize = AtomicUsize::new(0); | 371 | | | 372 | 1 | let collector = Collector::new(); | 373 | 1 | let handle = collector.register(); | 374 | | | 375 | | unsafe { | 376 | 1 | let guard = &handle.pin(); | 377 | 1 | | 378 | 1 | let mut v = Vec::with_capacity(COUNT); | 379 | 100k | for i in 0..COUNT { | 380 | 100k | v.push(i as i32); | 381 | 100k | } | 382 | | | 383 | 1 | let ptr = v.as_mut_ptr() as usize; | 384 | 1 | let len = v.len(); | 385 | 1 | guard.defer_unchecked(move || { | 386 | | drop(Vec::from_raw_parts(ptr as *const i32 as *mut i32, len, len)); | 387 | | DESTROYS.fetch_add(len, Ordering::Relaxed); | 388 | 1 | }); | 389 | 1 | guard.flush(); | 390 | 1 | | 391 | 1 | mem::forget(v); | 392 | | } | 393 | | | 394 | 3 | while DESTROYS.load(Ordering::Relaxed) < COUNT { | 395 | 2 | let guard = &handle.pin(); | 396 | 2 | collector.global.collect(guard); | 397 | 2 | } | 398 | 1 | assert_eq!(DESTROYS.load(Ordering::Relaxed), COUNT); | 399 | 1 | } |
|
400 | | |
401 | | #[test] |
402 | 1 | fn stress() { crossbeam_epoch::collector::tests::stress::{closure#0} Line | Count | Source | 402 | 1 | fn stress() { |
|
403 | | const THREADS: usize = 8; |
404 | | const COUNT: usize = 100_000; |
405 | | static DROPS: AtomicUsize = AtomicUsize::new(0); |
406 | | |
407 | | struct Elem(i32); |
408 | | |
409 | | impl Drop for Elem { |
410 | 685k | fn drop(&mut self) { |
411 | 685k | DROPS.fetch_add(1, Ordering::Relaxed); |
412 | 685k | } |
413 | | } |
414 | | |
415 | 1 | let collector = Collector::new(); |
416 | 1 | |
417 | 1 | thread::scope(|scope| { |
418 | 9 | for _8 in 0..THREADS { |
419 | 8 | scope.spawn(|_| { |
420 | 8 | let handle = collector.register(); |
421 | 722k | for _722k in 0..COUNT { |
422 | 722k | let guard = &handle.pin(); |
423 | 722k | unsafe { |
424 | 722k | let a = Owned::new(Elem(7i32)).into_shared(guard); |
425 | 722k | guard.defer_destroy(a); |
426 | 722k | } |
427 | | } |
428 | 8 | }); |
429 | 8 | } |
430 | 1 | }) |
431 | 1 | .unwrap(); |
432 | 1 | |
433 | 1 | let handle = collector.register(); |
434 | 3 | while DROPS.load(Ordering::Relaxed) < COUNT * THREADS { |
435 | 2 | let guard = &handle.pin(); |
436 | 2 | collector.global.collect(guard); |
437 | 2 | } |
438 | 1 | assert_eq!(DROPS.load(Ordering::Relaxed), COUNT * THREADS); |
439 | 1 | } crossbeam_epoch::collector::tests::stress Line | Count | Source | 402 | 1 | fn stress() { | 403 | | const THREADS: usize = 8; | 404 | | const COUNT: usize = 100_000; | 405 | | static DROPS: AtomicUsize = AtomicUsize::new(0); | 406 | | | 407 | | struct Elem(i32); | 408 | | | 409 | | impl Drop for Elem { | 410 | | fn drop(&mut self) { | 411 | | DROPS.fetch_add(1, Ordering::Relaxed); | 412 | | } | 413 | | } | 414 | | | 415 | 1 | let collector = Collector::new(); | 416 | 1 | | 417 | 1 | thread::scope(|scope| { | 418 | | for _ in 0..THREADS { | 419 | | scope.spawn(|_| { | 420 | | let handle = collector.register(); | 421 | | for _ in 0..COUNT { | 422 | | let guard = &handle.pin(); | 423 | | unsafe { | 424 | | let a = Owned::new(Elem(7i32)).into_shared(guard); | 425 | | guard.defer_destroy(a); | 426 | | } | 427 | | } | 428 | | }); | 429 | | } | 430 | 1 | }) | 431 | 1 | .unwrap(); | 432 | 1 | | 433 | 1 | let handle = collector.register(); | 434 | 3 | while DROPS.load(Ordering::Relaxed) < COUNT * THREADS { | 435 | 2 | let guard = &handle.pin(); | 436 | 2 | collector.global.collect(guard); | 437 | 2 | } | 438 | 1 | assert_eq!(DROPS.load(Ordering::Relaxed), COUNT * THREADS); | 439 | 1 | } |
|
440 | | } |