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(ThreadID _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(
232 ThreadID os_id) {
233 return FindThreadContextLocked(cb: FindThreadContextByOsIdCallback,
234 arg: (void *)os_id);
235}
236
237void ThreadRegistry::SetThreadName(u32 tid, const char *name) {
238 ThreadRegistryLock l(this);
239 ThreadContextBase *tctx = threads_[tid];
240 CHECK_NE(tctx, 0);
241 CHECK_EQ(SANITIZER_FUCHSIA ? ThreadStatusCreated : ThreadStatusRunning,
242 tctx->status);
243 tctx->SetName(name);
244}
245
246void ThreadRegistry::SetThreadNameByUserId(uptr user_id, const char *name) {
247 ThreadRegistryLock l(this);
248 if (const auto *tid = live_.find(Key: user_id))
249 threads_[tid->second]->SetName(name);
250}
251
252void ThreadRegistry::DetachThread(u32 tid, void *arg) {
253 ThreadRegistryLock l(this);
254 ThreadContextBase *tctx = threads_[tid];
255 CHECK_NE(tctx, 0);
256 if (tctx->status == ThreadStatusInvalid) {
257 Report(format: "%s: Detach of non-existent thread\n", SanitizerToolName);
258 return;
259 }
260 tctx->OnDetached(arg);
261 if (tctx->status == ThreadStatusFinished) {
262 if (tctx->user_id)
263 live_.erase(Val: tctx->user_id);
264 tctx->SetDead();
265 QuarantinePush(tctx);
266 } else {
267 tctx->detached = true;
268 }
269}
270
271void ThreadRegistry::JoinThread(u32 tid, void *arg) {
272 bool destroyed = false;
273 do {
274 {
275 ThreadRegistryLock l(this);
276 ThreadContextBase *tctx = threads_[tid];
277 CHECK_NE(tctx, 0);
278 if (tctx->status == ThreadStatusInvalid) {
279 Report(format: "%s: Join of non-existent thread\n", SanitizerToolName);
280 return;
281 }
282 if ((destroyed = tctx->GetDestroyed())) {
283 if (tctx->user_id)
284 live_.erase(Val: tctx->user_id);
285 tctx->SetJoined(arg);
286 QuarantinePush(tctx);
287 }
288 }
289 if (!destroyed)
290 internal_sched_yield();
291 } while (!destroyed);
292}
293
294// Normally this is called when the thread is about to exit. If
295// called in ThreadStatusCreated state, then this thread was never
296// really started. We just did CreateThread for a prospective new
297// thread before trying to create it, and then failed to actually
298// create it, and so never called StartThread.
299ThreadStatus ThreadRegistry::FinishThread(u32 tid) {
300 ThreadRegistryLock l(this);
301 CHECK_GT(alive_threads_, 0);
302 alive_threads_--;
303 ThreadContextBase *tctx = threads_[tid];
304 CHECK_NE(tctx, 0);
305 bool dead = tctx->detached;
306 ThreadStatus prev_status = tctx->status;
307 if (tctx->status == ThreadStatusRunning) {
308 CHECK_GT(running_threads_, 0);
309 running_threads_--;
310 } else {
311 // The thread never really existed.
312 CHECK_EQ(tctx->status, ThreadStatusCreated);
313 dead = true;
314 }
315 tctx->SetFinished();
316 if (dead) {
317 if (tctx->user_id)
318 live_.erase(Val: tctx->user_id);
319 tctx->SetDead();
320 QuarantinePush(tctx);
321 }
322 tctx->SetDestroyed();
323 return prev_status;
324}
325
326void ThreadRegistry::StartThread(u32 tid, ThreadID os_id,
327 ThreadType thread_type, void *arg) {
328 ThreadRegistryLock l(this);
329 running_threads_++;
330 ThreadContextBase *tctx = threads_[tid];
331 CHECK_NE(tctx, 0);
332 CHECK_EQ(ThreadStatusCreated, tctx->status);
333 tctx->SetStarted(os_id: os_id, thread_type: thread_type, arg);
334}
335
336void ThreadRegistry::QuarantinePush(ThreadContextBase *tctx) {
337 if (tctx->tid == 0)
338 return; // Don't reuse the main thread. It's a special snowflake.
339 dead_threads_.push_back(x: tctx);
340 if (dead_threads_.size() <= thread_quarantine_size_)
341 return;
342 tctx = dead_threads_.front();
343 dead_threads_.pop_front();
344 CHECK_EQ(tctx->status, ThreadStatusDead);
345 tctx->Reset();
346 tctx->reuse_count++;
347 if (max_reuse_ > 0 && tctx->reuse_count >= max_reuse_)
348 return;
349 invalid_threads_.push_back(x: tctx);
350}
351
352ThreadContextBase *ThreadRegistry::QuarantinePop() {
353 if (invalid_threads_.size() == 0)
354 return nullptr;
355 ThreadContextBase *tctx = invalid_threads_.front();
356 invalid_threads_.pop_front();
357 return tctx;
358}
359
360u32 ThreadRegistry::ConsumeThreadUserId(uptr user_id) {
361 ThreadRegistryLock l(this);
362 u32 tid;
363 auto *t = live_.find(Key: user_id);
364 CHECK(t);
365 tid = t->second;
366 live_.erase(I: t);
367 auto *tctx = threads_[tid];
368 CHECK_EQ(tctx->user_id, user_id);
369 tctx->user_id = 0;
370 return tid;
371}
372
373void ThreadRegistry::SetThreadUserId(u32 tid, uptr user_id) {
374 ThreadRegistryLock l(this);
375 ThreadContextBase *tctx = threads_[tid];
376 CHECK_NE(tctx, 0);
377 CHECK_NE(tctx->status, ThreadStatusInvalid);
378 CHECK_NE(tctx->status, ThreadStatusDead);
379 CHECK_EQ(tctx->user_id, 0);
380 tctx->user_id = user_id;
381 CHECK(live_.try_emplace(user_id, tctx->tid).second);
382}
383
384u32 ThreadRegistry::OnFork(u32 tid) {
385 ThreadRegistryLock l(this);
386 // We only purge user_id (pthread_t) of live threads because
387 // they cause CHECK failures if new threads with matching pthread_t
388 // created after fork.
389 // Potentially we could purge more info (ThreadContextBase themselves),
390 // but it's hard to test and easy to introduce new issues by doing this.
391 for (auto *tctx : threads_) {
392 if (tctx->tid == tid || !tctx->user_id)
393 continue;
394 CHECK(live_.erase(tctx->user_id));
395 tctx->user_id = 0;
396 }
397 return alive_threads_;
398}
399
400} // namespace __sanitizer
401