1 | //===-- sanitizer_thread_registry.cpp -------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This file is shared between sanitizer tools. |
10 | // |
11 | // General thread bookkeeping functionality. |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "sanitizer_thread_registry.h" |
15 | |
16 | #include "sanitizer_placement_new.h" |
17 | |
18 | namespace __sanitizer { |
19 | |
20 | ThreadContextBase::ThreadContextBase(u32 tid) |
21 | : tid(tid), unique_id(0), reuse_count(), os_id(0), user_id(0), |
22 | status(ThreadStatusInvalid), detached(false), |
23 | thread_type(ThreadType::Regular), parent_tid(0), next(0) { |
24 | name[0] = '\0'; |
25 | atomic_store(a: &thread_destroyed, v: 0, mo: memory_order_release); |
26 | } |
27 | |
28 | ThreadContextBase::~ThreadContextBase() { |
29 | // ThreadContextBase should never be deleted. |
30 | CHECK(0); |
31 | } |
32 | |
33 | void ThreadContextBase::SetName(const char *new_name) { |
34 | name[0] = '\0'; |
35 | if (new_name) { |
36 | internal_strncpy(dst: name, src: new_name, n: sizeof(name)); |
37 | name[sizeof(name) - 1] = '\0'; |
38 | } |
39 | } |
40 | |
41 | void ThreadContextBase::SetDead() { |
42 | CHECK(status == ThreadStatusRunning || |
43 | status == ThreadStatusFinished); |
44 | status = ThreadStatusDead; |
45 | user_id = 0; |
46 | OnDead(); |
47 | } |
48 | |
49 | void ThreadContextBase::SetDestroyed() { |
50 | atomic_store(a: &thread_destroyed, v: 1, mo: memory_order_release); |
51 | } |
52 | |
53 | bool ThreadContextBase::GetDestroyed() { |
54 | return !!atomic_load(a: &thread_destroyed, mo: memory_order_acquire); |
55 | } |
56 | |
57 | void ThreadContextBase::SetJoined(void *arg) { |
58 | // FIXME(dvyukov): print message and continue (it's user error). |
59 | CHECK_EQ(false, detached); |
60 | CHECK_EQ(ThreadStatusFinished, status); |
61 | status = ThreadStatusDead; |
62 | user_id = 0; |
63 | OnJoined(arg); |
64 | } |
65 | |
66 | void ThreadContextBase::SetFinished() { |
67 | // ThreadRegistry::FinishThread calls here in ThreadStatusCreated state |
68 | // for a thread that never actually started. In that case the thread |
69 | // should go to ThreadStatusFinished regardless of whether it was created |
70 | // as detached. |
71 | if (!detached || status == ThreadStatusCreated) status = ThreadStatusFinished; |
72 | OnFinished(); |
73 | } |
74 | |
75 | void ThreadContextBase::SetStarted(tid_t _os_id, ThreadType _thread_type, |
76 | void *arg) { |
77 | status = ThreadStatusRunning; |
78 | os_id = _os_id; |
79 | thread_type = _thread_type; |
80 | OnStarted(arg); |
81 | } |
82 | |
83 | void ThreadContextBase::SetCreated(uptr _user_id, u64 _unique_id, |
84 | bool _detached, u32 _parent_tid, void *arg) { |
85 | status = ThreadStatusCreated; |
86 | user_id = _user_id; |
87 | unique_id = _unique_id; |
88 | detached = _detached; |
89 | // Parent tid makes no sense for the main thread. |
90 | if (tid != kMainTid) |
91 | parent_tid = _parent_tid; |
92 | OnCreated(arg); |
93 | } |
94 | |
95 | void ThreadContextBase::Reset() { |
96 | status = ThreadStatusInvalid; |
97 | SetName(0); |
98 | atomic_store(a: &thread_destroyed, v: 0, mo: memory_order_release); |
99 | OnReset(); |
100 | } |
101 | |
102 | // ThreadRegistry implementation. |
103 | |
104 | ThreadRegistry::ThreadRegistry(ThreadContextFactory factory) |
105 | : ThreadRegistry(factory, UINT32_MAX, UINT32_MAX, 0) {} |
106 | |
107 | ThreadRegistry::ThreadRegistry(ThreadContextFactory factory, u32 max_threads, |
108 | u32 thread_quarantine_size, u32 max_reuse) |
109 | : context_factory_(factory), |
110 | max_threads_(max_threads), |
111 | thread_quarantine_size_(thread_quarantine_size), |
112 | max_reuse_(max_reuse), |
113 | mtx_(MutexThreadRegistry), |
114 | total_threads_(0), |
115 | alive_threads_(0), |
116 | max_alive_threads_(0), |
117 | running_threads_(0) { |
118 | dead_threads_.clear(); |
119 | invalid_threads_.clear(); |
120 | } |
121 | |
122 | void ThreadRegistry::GetNumberOfThreads(uptr *total, uptr *running, |
123 | uptr *alive) { |
124 | ThreadRegistryLock l(this); |
125 | if (total) |
126 | *total = threads_.size(); |
127 | if (running) *running = running_threads_; |
128 | if (alive) *alive = alive_threads_; |
129 | } |
130 | |
131 | uptr ThreadRegistry::GetMaxAliveThreads() { |
132 | ThreadRegistryLock l(this); |
133 | return max_alive_threads_; |
134 | } |
135 | |
136 | u32 ThreadRegistry::CreateThread(uptr user_id, bool detached, u32 parent_tid, |
137 | void *arg) { |
138 | ThreadRegistryLock l(this); |
139 | u32 tid = kInvalidTid; |
140 | ThreadContextBase *tctx = QuarantinePop(); |
141 | if (tctx) { |
142 | tid = tctx->tid; |
143 | } else if (threads_.size() < max_threads_) { |
144 | // Allocate new thread context and tid. |
145 | tid = threads_.size(); |
146 | tctx = context_factory_(tid); |
147 | threads_.push_back(element: tctx); |
148 | } else { |
149 | #if !SANITIZER_GO |
150 | Report(format: "%s: Thread limit (%u threads) exceeded. Dying.\n" , |
151 | SanitizerToolName, max_threads_); |
152 | #else |
153 | Printf("race: limit on %u simultaneously alive goroutines is exceeded," |
154 | " dying\n" , max_threads_); |
155 | #endif |
156 | Die(); |
157 | } |
158 | CHECK_NE(tctx, 0); |
159 | CHECK_NE(tid, kInvalidTid); |
160 | CHECK_LT(tid, max_threads_); |
161 | CHECK_EQ(tctx->status, ThreadStatusInvalid); |
162 | alive_threads_++; |
163 | if (max_alive_threads_ < alive_threads_) { |
164 | max_alive_threads_++; |
165 | CHECK_EQ(alive_threads_, max_alive_threads_); |
166 | } |
167 | if (user_id) { |
168 | // Ensure that user_id is unique. If it's not the case we are screwed. |
169 | // Ignoring this situation may lead to very hard to debug false |
170 | // positives later (e.g. if we join a wrong thread). |
171 | CHECK(live_.try_emplace(user_id, tid).second); |
172 | } |
173 | tctx->SetCreated(user_id: user_id, unique_id: total_threads_++, detached: detached, |
174 | parent_tid: parent_tid, arg); |
175 | return tid; |
176 | } |
177 | |
178 | void ThreadRegistry::RunCallbackForEachThreadLocked(ThreadCallback cb, |
179 | void *arg) { |
180 | CheckLocked(); |
181 | for (u32 tid = 0; tid < threads_.size(); tid++) { |
182 | ThreadContextBase *tctx = threads_[tid]; |
183 | if (tctx == 0) |
184 | continue; |
185 | cb(tctx, arg); |
186 | } |
187 | } |
188 | |
189 | u32 ThreadRegistry::FindThread(FindThreadCallback cb, void *arg) { |
190 | ThreadRegistryLock l(this); |
191 | for (u32 tid = 0; tid < threads_.size(); tid++) { |
192 | ThreadContextBase *tctx = threads_[tid]; |
193 | if (tctx != 0 && cb(tctx, arg)) |
194 | return tctx->tid; |
195 | } |
196 | return kInvalidTid; |
197 | } |
198 | |
199 | ThreadContextBase * |
200 | ThreadRegistry::FindThreadContextLocked(FindThreadCallback cb, void *arg) { |
201 | CheckLocked(); |
202 | for (u32 tid = 0; tid < threads_.size(); tid++) { |
203 | ThreadContextBase *tctx = threads_[tid]; |
204 | if (tctx != 0 && cb(tctx, arg)) |
205 | return tctx; |
206 | } |
207 | return 0; |
208 | } |
209 | |
210 | static bool FindThreadContextByOsIdCallback(ThreadContextBase *tctx, |
211 | void *arg) { |
212 | return (tctx->os_id == (uptr)arg && tctx->status != ThreadStatusInvalid && |
213 | tctx->status != ThreadStatusDead); |
214 | } |
215 | |
216 | ThreadContextBase *ThreadRegistry::FindThreadContextByOsIDLocked(tid_t os_id) { |
217 | return FindThreadContextLocked(cb: FindThreadContextByOsIdCallback, |
218 | arg: (void *)os_id); |
219 | } |
220 | |
221 | void ThreadRegistry::SetThreadName(u32 tid, const char *name) { |
222 | ThreadRegistryLock l(this); |
223 | ThreadContextBase *tctx = threads_[tid]; |
224 | CHECK_NE(tctx, 0); |
225 | CHECK_EQ(SANITIZER_FUCHSIA ? ThreadStatusCreated : ThreadStatusRunning, |
226 | tctx->status); |
227 | tctx->SetName(name); |
228 | } |
229 | |
230 | void ThreadRegistry::SetThreadNameByUserId(uptr user_id, const char *name) { |
231 | ThreadRegistryLock l(this); |
232 | if (const auto *tid = live_.find(Key: user_id)) |
233 | threads_[tid->second]->SetName(name); |
234 | } |
235 | |
236 | void ThreadRegistry::DetachThread(u32 tid, void *arg) { |
237 | ThreadRegistryLock l(this); |
238 | ThreadContextBase *tctx = threads_[tid]; |
239 | CHECK_NE(tctx, 0); |
240 | if (tctx->status == ThreadStatusInvalid) { |
241 | Report(format: "%s: Detach of non-existent thread\n" , SanitizerToolName); |
242 | return; |
243 | } |
244 | tctx->OnDetached(arg); |
245 | if (tctx->status == ThreadStatusFinished) { |
246 | if (tctx->user_id) |
247 | live_.erase(Val: tctx->user_id); |
248 | tctx->SetDead(); |
249 | QuarantinePush(tctx); |
250 | } else { |
251 | tctx->detached = true; |
252 | } |
253 | } |
254 | |
255 | void ThreadRegistry::JoinThread(u32 tid, void *arg) { |
256 | bool destroyed = false; |
257 | do { |
258 | { |
259 | ThreadRegistryLock l(this); |
260 | ThreadContextBase *tctx = threads_[tid]; |
261 | CHECK_NE(tctx, 0); |
262 | if (tctx->status == ThreadStatusInvalid) { |
263 | Report(format: "%s: Join of non-existent thread\n" , SanitizerToolName); |
264 | return; |
265 | } |
266 | if ((destroyed = tctx->GetDestroyed())) { |
267 | if (tctx->user_id) |
268 | live_.erase(Val: tctx->user_id); |
269 | tctx->SetJoined(arg); |
270 | QuarantinePush(tctx); |
271 | } |
272 | } |
273 | if (!destroyed) |
274 | internal_sched_yield(); |
275 | } while (!destroyed); |
276 | } |
277 | |
278 | // Normally this is called when the thread is about to exit. If |
279 | // called in ThreadStatusCreated state, then this thread was never |
280 | // really started. We just did CreateThread for a prospective new |
281 | // thread before trying to create it, and then failed to actually |
282 | // create it, and so never called StartThread. |
283 | ThreadStatus ThreadRegistry::FinishThread(u32 tid) { |
284 | ThreadRegistryLock l(this); |
285 | CHECK_GT(alive_threads_, 0); |
286 | alive_threads_--; |
287 | ThreadContextBase *tctx = threads_[tid]; |
288 | CHECK_NE(tctx, 0); |
289 | bool dead = tctx->detached; |
290 | ThreadStatus prev_status = tctx->status; |
291 | if (tctx->status == ThreadStatusRunning) { |
292 | CHECK_GT(running_threads_, 0); |
293 | running_threads_--; |
294 | } else { |
295 | // The thread never really existed. |
296 | CHECK_EQ(tctx->status, ThreadStatusCreated); |
297 | dead = true; |
298 | } |
299 | tctx->SetFinished(); |
300 | if (dead) { |
301 | if (tctx->user_id) |
302 | live_.erase(Val: tctx->user_id); |
303 | tctx->SetDead(); |
304 | QuarantinePush(tctx); |
305 | } |
306 | tctx->SetDestroyed(); |
307 | return prev_status; |
308 | } |
309 | |
310 | void ThreadRegistry::StartThread(u32 tid, tid_t os_id, ThreadType thread_type, |
311 | void *arg) { |
312 | ThreadRegistryLock l(this); |
313 | running_threads_++; |
314 | ThreadContextBase *tctx = threads_[tid]; |
315 | CHECK_NE(tctx, 0); |
316 | CHECK_EQ(ThreadStatusCreated, tctx->status); |
317 | tctx->SetStarted(os_id: os_id, thread_type: thread_type, arg); |
318 | } |
319 | |
320 | void ThreadRegistry::QuarantinePush(ThreadContextBase *tctx) { |
321 | if (tctx->tid == 0) |
322 | return; // Don't reuse the main thread. It's a special snowflake. |
323 | dead_threads_.push_back(x: tctx); |
324 | if (dead_threads_.size() <= thread_quarantine_size_) |
325 | return; |
326 | tctx = dead_threads_.front(); |
327 | dead_threads_.pop_front(); |
328 | CHECK_EQ(tctx->status, ThreadStatusDead); |
329 | tctx->Reset(); |
330 | tctx->reuse_count++; |
331 | if (max_reuse_ > 0 && tctx->reuse_count >= max_reuse_) |
332 | return; |
333 | invalid_threads_.push_back(x: tctx); |
334 | } |
335 | |
336 | ThreadContextBase *ThreadRegistry::QuarantinePop() { |
337 | if (invalid_threads_.size() == 0) |
338 | return nullptr; |
339 | ThreadContextBase *tctx = invalid_threads_.front(); |
340 | invalid_threads_.pop_front(); |
341 | return tctx; |
342 | } |
343 | |
344 | u32 ThreadRegistry::ConsumeThreadUserId(uptr user_id) { |
345 | ThreadRegistryLock l(this); |
346 | u32 tid; |
347 | auto *t = live_.find(Key: user_id); |
348 | CHECK(t); |
349 | tid = t->second; |
350 | live_.erase(I: t); |
351 | auto *tctx = threads_[tid]; |
352 | CHECK_EQ(tctx->user_id, user_id); |
353 | tctx->user_id = 0; |
354 | return tid; |
355 | } |
356 | |
357 | void ThreadRegistry::SetThreadUserId(u32 tid, uptr user_id) { |
358 | ThreadRegistryLock l(this); |
359 | ThreadContextBase *tctx = threads_[tid]; |
360 | CHECK_NE(tctx, 0); |
361 | CHECK_NE(tctx->status, ThreadStatusInvalid); |
362 | CHECK_NE(tctx->status, ThreadStatusDead); |
363 | CHECK_EQ(tctx->user_id, 0); |
364 | tctx->user_id = user_id; |
365 | CHECK(live_.try_emplace(user_id, tctx->tid).second); |
366 | } |
367 | |
368 | u32 ThreadRegistry::OnFork(u32 tid) { |
369 | ThreadRegistryLock l(this); |
370 | // We only purge user_id (pthread_t) of live threads because |
371 | // they cause CHECK failures if new threads with matching pthread_t |
372 | // created after fork. |
373 | // Potentially we could purge more info (ThreadContextBase themselves), |
374 | // but it's hard to test and easy to introduce new issues by doing this. |
375 | for (auto *tctx : threads_) { |
376 | if (tctx->tid == tid || !tctx->user_id) |
377 | continue; |
378 | CHECK(live_.erase(tctx->user_id)); |
379 | tctx->user_id = 0; |
380 | } |
381 | return alive_threads_; |
382 | } |
383 | |
384 | } // namespace __sanitizer |
385 | |