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 
collector() -> &'static Collector13 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]
pin() -> Guard39 pub fn pin() -> Guard {
40     with_handle(|handle| handle.pin())
41 }
42 
43 /// Returns `true` if the current thread is pinned.
44 #[inline]
is_pinned() -> bool45 pub fn is_pinned() -> bool {
46     with_handle(|handle| handle.is_pinned())
47 }
48 
49 /// Returns the default global collector.
default_collector() -> &'static Collector50 pub fn default_collector() -> &'static Collector {
51     collector()
52 }
53 
54 #[inline]
with_handle<F, R>(mut f: F) -> R where F: FnMut(&LocalHandle) -> R,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]
pin_while_exiting()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 = const { 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