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
18namespace __sanitizer {
19
20ThreadContextBase::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
28ThreadContextBase::~ThreadContextBase() {
29 // ThreadContextBase should never be deleted.
30 CHECK(0);
31}
32
33void 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
41void ThreadContextBase::SetDead() {
42 CHECK(status == ThreadStatusRunning ||
43 status == ThreadStatusFinished);
44 status = ThreadStatusDead;
45 user_id = 0;
46 OnDead();
47}
48
49void ThreadContextBase::SetDestroyed() {
50 atomic_store(a: &thread_destroyed, v: 1, mo: memory_order_release);
51}
52
53bool ThreadContextBase::GetDestroyed() {
54 return !!atomic_load(a: &thread_destroyed, mo: memory_order_acquire);
55}
56
57void 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
66void 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
75void 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
83void 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
95void 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
104ThreadRegistry::ThreadRegistry(ThreadContextFactory factory)
105 : ThreadRegistry(factory, UINT32_MAX, UINT32_MAX, 0) {}
106
107ThreadRegistry::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
122void 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
131uptr ThreadRegistry::GetMaxAliveThreads() {
132 ThreadRegistryLock l(this);
133 return max_alive_threads_;
134}
135
136u32 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
178void 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
189u32 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
199ThreadContextBase *
200ThreadRegistry::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
210static bool FindThreadContextByOsIdCallback(ThreadContextBase *tctx,
211 void *arg) {
212 return (tctx->os_id == (uptr)arg && tctx->status != ThreadStatusInvalid &&
213 tctx->status != ThreadStatusDead);
214}
215
216ThreadContextBase *ThreadRegistry::FindThreadContextByOsIDLocked(tid_t os_id) {
217 return FindThreadContextLocked(cb: FindThreadContextByOsIdCallback,
218 arg: (void *)os_id);
219}
220
221void 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
230void 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
236void 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
255void 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.
283ThreadStatus 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
310void 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
320void 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
336ThreadContextBase *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
344u32 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
357void 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
368u32 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

source code of compiler-rt/lib/sanitizer_common/sanitizer_thread_registry.cpp