1 use crate::{
2     cfg::{self, CfgPrivate},
3     page,
4     sync::{
5         atomic::{AtomicUsize, Ordering},
6         lazy_static, thread_local, Mutex,
7     },
8     Pack,
9 };
10 use std::{
11     cell::{Cell, UnsafeCell},
12     collections::VecDeque,
13     fmt,
14     marker::PhantomData,
15 };
16 
17 /// Uniquely identifies a thread.
18 pub(crate) struct Tid<C> {
19     id: usize,
20     _not_send: PhantomData<UnsafeCell<()>>,
21     _cfg: PhantomData<fn(C)>,
22 }
23 
24 #[derive(Debug)]
25 struct Registration(Cell<Option<usize>>);
26 
27 struct Registry {
28     next: AtomicUsize,
29     free: Mutex<VecDeque<usize>>,
30 }
31 
32 lazy_static! {
33     static ref REGISTRY: Registry = Registry {
34         next: AtomicUsize::new(0),
35         free: Mutex::new(VecDeque::new()),
36     };
37 }
38 
39 thread_local! {
40     static REGISTRATION: Registration = Registration::new();
41 }
42 
43 // === impl Tid ===
44 
45 impl<C: cfg::Config> Pack<C> for Tid<C> {
46     const LEN: usize = C::MAX_SHARDS.trailing_zeros() as usize + 1;
47 
48     type Prev = page::Addr<C>;
49 
50     #[inline(always)]
as_usize(&self) -> usize51     fn as_usize(&self) -> usize {
52         self.id
53     }
54 
55     #[inline(always)]
from_usize(id: usize) -> Self56     fn from_usize(id: usize) -> Self {
57         Self {
58             id,
59             _not_send: PhantomData,
60             _cfg: PhantomData,
61         }
62     }
63 }
64 
65 impl<C: cfg::Config> Tid<C> {
66     #[inline]
current() -> Self67     pub(crate) fn current() -> Self {
68         REGISTRATION
69             .try_with(Registration::current)
70             .unwrap_or_else(|_| Self::poisoned())
71     }
72 
is_current(self) -> bool73     pub(crate) fn is_current(self) -> bool {
74         REGISTRATION
75             .try_with(|r| self == r.current::<C>())
76             .unwrap_or(false)
77     }
78 
79     #[inline(always)]
new(id: usize) -> Self80     pub fn new(id: usize) -> Self {
81         Self::from_usize(id)
82     }
83 }
84 
85 impl<C> Tid<C> {
86     #[cold]
poisoned() -> Self87     fn poisoned() -> Self {
88         Self {
89             id: std::usize::MAX,
90             _not_send: PhantomData,
91             _cfg: PhantomData,
92         }
93     }
94 
95     /// Returns true if the local thread ID was accessed while unwinding.
is_poisoned(&self) -> bool96     pub(crate) fn is_poisoned(&self) -> bool {
97         self.id == std::usize::MAX
98     }
99 }
100 
101 impl<C> fmt::Debug for Tid<C> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result102     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103         if self.is_poisoned() {
104             f.debug_tuple("Tid")
105                 .field(&format_args!("<poisoned>"))
106                 .finish()
107         } else {
108             f.debug_tuple("Tid")
109                 .field(&format_args!("{}", self.id))
110                 .finish()
111         }
112     }
113 }
114 
115 impl<C> PartialEq for Tid<C> {
eq(&self, other: &Self) -> bool116     fn eq(&self, other: &Self) -> bool {
117         self.id == other.id
118     }
119 }
120 
121 impl<C> Eq for Tid<C> {}
122 
123 impl<C: cfg::Config> Clone for Tid<C> {
clone(&self) -> Self124     fn clone(&self) -> Self {
125         *self
126     }
127 }
128 
129 impl<C: cfg::Config> Copy for Tid<C> {}
130 
131 // === impl Registration ===
132 
133 impl Registration {
new() -> Self134     fn new() -> Self {
135         Self(Cell::new(None))
136     }
137 
138     #[inline(always)]
current<C: cfg::Config>(&self) -> Tid<C>139     fn current<C: cfg::Config>(&self) -> Tid<C> {
140         if let Some(tid) = self.0.get().map(Tid::new) {
141             return tid;
142         }
143 
144         self.register()
145     }
146 
147     #[cold]
register<C: cfg::Config>(&self) -> Tid<C>148     fn register<C: cfg::Config>(&self) -> Tid<C> {
149         let id = REGISTRY
150             .free
151             .lock()
152             .ok()
153             .and_then(|mut free| {
154                 if free.len() > 1 {
155                     free.pop_front()
156                 } else {
157                     None
158                 }
159             })
160             .unwrap_or_else(|| {
161                 let id = REGISTRY.next.fetch_add(1, Ordering::AcqRel);
162                 if id > Tid::<C>::BITS {
163                     panic_in_drop!(
164                         "creating a new thread ID ({}) would exceed the \
165                         maximum number of thread ID bits specified in {} \
166                         ({})",
167                         id,
168                         std::any::type_name::<C>(),
169                         Tid::<C>::BITS,
170                     );
171                 }
172                 id
173             });
174 
175         self.0.set(Some(id));
176         Tid::new(id)
177     }
178 }
179 
180 // Reusing thread IDs doesn't work under loom, since this `Drop` impl results in
181 // an access to a `loom` lazy_static while the test is shutting down, which
182 // panics. T_T
183 // Just skip TID reuse and use loom's lazy_static macro to ensure we have a
184 // clean initial TID on every iteration, instead.
185 #[cfg(not(all(loom, any(feature = "loom", test))))]
186 impl Drop for Registration {
drop(&mut self)187     fn drop(&mut self) {
188         use std::sync::PoisonError;
189 
190         if let Some(id) = self.0.get() {
191             let mut free_list = REGISTRY.free.lock().unwrap_or_else(PoisonError::into_inner);
192             free_list.push_back(id);
193         }
194     }
195 }
196 
197 #[cfg(all(test, not(loom)))]
with<R>(tid: usize, f: impl FnOnce() -> R) -> R198 pub(crate) fn with<R>(tid: usize, f: impl FnOnce() -> R) -> R {
199     struct Guard(Option<usize>);
200 
201     impl Drop for Guard {
202         fn drop(&mut self) {
203             REGISTRATION.with(|r| r.0.set(self.0.take()));
204         }
205     }
206 
207     let prev = REGISTRATION.with(|r| r.0.replace(Some(tid)));
208     let _guard = Guard(prev);
209     f()
210 }
211