1 | //! The default garbage collector. |
2 | //! |
3 | //! For each thread, a participant is lazily initialized on its first use, when the current thread |
4 | //! is registered in the default collector. If initialized, the thread's participant will get |
5 | //! destructed on thread exit, which in turn unregisters the thread. |
6 | |
7 | use crate::collector::{Collector, LocalHandle}; |
8 | use crate::guard::Guard; |
9 | use crate::primitive::thread_local; |
10 | #[cfg (not(crossbeam_loom))] |
11 | use crate::sync::once_lock::OnceLock; |
12 | |
13 | fn collector() -> &'static Collector { |
14 | #[cfg (not(crossbeam_loom))] |
15 | { |
16 | /// The global data for the default garbage collector. |
17 | static COLLECTOR: OnceLock<Collector> = OnceLock::new(); |
18 | COLLECTOR.get_or_init(Collector::new) |
19 | } |
20 | // FIXME: loom does not currently provide the equivalent of Lazy: |
21 | // https://github.com/tokio-rs/loom/issues/263 |
22 | #[cfg (crossbeam_loom)] |
23 | { |
24 | loom::lazy_static! { |
25 | /// The global data for the default garbage collector. |
26 | static ref COLLECTOR: Collector = Collector::new(); |
27 | } |
28 | &COLLECTOR |
29 | } |
30 | } |
31 | |
32 | thread_local! { |
33 | /// The per-thread participant for the default garbage collector. |
34 | static HANDLE: LocalHandle = collector().register(); |
35 | } |
36 | |
37 | /// Pins the current thread. |
38 | #[inline ] |
39 | pub fn pin() -> Guard { |
40 | with_handle(|handle: &LocalHandle| handle.pin()) |
41 | } |
42 | |
43 | /// Returns `true` if the current thread is pinned. |
44 | #[inline ] |
45 | pub fn is_pinned() -> bool { |
46 | with_handle(|handle: &LocalHandle| handle.is_pinned()) |
47 | } |
48 | |
49 | /// Returns the default global collector. |
50 | pub fn default_collector() -> &'static Collector { |
51 | collector() |
52 | } |
53 | |
54 | #[inline ] |
55 | fn with_handle<F, R>(mut f: F) -> R |
56 | where |
57 | F: FnMut(&LocalHandle) -> R, |
58 | { |
59 | HANDLE |
60 | .try_with(|h| f(h)) |
61 | .unwrap_or_else(|_| f(&collector().register())) |
62 | } |
63 | |
64 | #[cfg (all(test, not(crossbeam_loom)))] |
65 | mod tests { |
66 | use crossbeam_utils::thread; |
67 | |
68 | #[test ] |
69 | fn pin_while_exiting() { |
70 | struct Foo; |
71 | |
72 | impl Drop for Foo { |
73 | fn drop(&mut self) { |
74 | // Pin after `HANDLE` has been dropped. This must not panic. |
75 | super::pin(); |
76 | } |
77 | } |
78 | |
79 | thread_local! { |
80 | static FOO: Foo = Foo; |
81 | } |
82 | |
83 | thread::scope(|scope| { |
84 | scope.spawn(|_| { |
85 | // Initialize `FOO` and then `HANDLE`. |
86 | FOO.with(|_| ()); |
87 | super::pin(); |
88 | // At thread exit, `HANDLE` gets dropped first and `FOO` second. |
89 | }); |
90 | }) |
91 | .unwrap(); |
92 | } |
93 | } |
94 | |