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