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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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