xref: /freebsd/contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_thread_registry.cpp (revision 06c3fb2749bda94cb5201f81ffdb8fa6c3161b2e)
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 
ThreadContextBase(u32 tid)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(&thread_destroyed, 0, memory_order_release);
26 }
27 
~ThreadContextBase()28 ThreadContextBase::~ThreadContextBase() {
29   // ThreadContextBase should never be deleted.
30   CHECK(0);
31 }
32 
SetName(const char * new_name)33 void ThreadContextBase::SetName(const char *new_name) {
34   name[0] = '\0';
35   if (new_name) {
36     internal_strncpy(name, new_name, sizeof(name));
37     name[sizeof(name) - 1] = '\0';
38   }
39 }
40 
SetDead()41 void ThreadContextBase::SetDead() {
42   CHECK(status == ThreadStatusRunning ||
43         status == ThreadStatusFinished);
44   status = ThreadStatusDead;
45   user_id = 0;
46   OnDead();
47 }
48 
SetDestroyed()49 void ThreadContextBase::SetDestroyed() {
50   atomic_store(&thread_destroyed, 1, memory_order_release);
51 }
52 
GetDestroyed()53 bool ThreadContextBase::GetDestroyed() {
54   return !!atomic_load(&thread_destroyed, memory_order_acquire);
55 }
56 
SetJoined(void * arg)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 
SetFinished()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 
SetStarted(tid_t _os_id,ThreadType _thread_type,void * arg)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 
SetCreated(uptr _user_id,u64 _unique_id,bool _detached,u32 _parent_tid,void * arg)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 
Reset()95 void ThreadContextBase::Reset() {
96   status = ThreadStatusInvalid;
97   SetName(0);
98   atomic_store(&thread_destroyed, 0, memory_order_release);
99   OnReset();
100 }
101 
102 // ThreadRegistry implementation.
103 
ThreadRegistry(ThreadContextFactory factory)104 ThreadRegistry::ThreadRegistry(ThreadContextFactory factory)
105     : ThreadRegistry(factory, UINT32_MAX, UINT32_MAX, 0) {}
106 
ThreadRegistry(ThreadContextFactory factory,u32 max_threads,u32 thread_quarantine_size,u32 max_reuse)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 
GetNumberOfThreads(uptr * total,uptr * running,uptr * alive)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 
GetMaxAliveThreads()131 uptr ThreadRegistry::GetMaxAliveThreads() {
132   ThreadRegistryLock l(this);
133   return max_alive_threads_;
134 }
135 
CreateThread(uptr user_id,bool detached,u32 parent_tid,void * arg)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(tctx);
148   } else {
149 #if !SANITIZER_GO
150     Report("%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, total_threads_++, detached,
174                    parent_tid, arg);
175   return tid;
176 }
177 
RunCallbackForEachThreadLocked(ThreadCallback cb,void * arg)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 
FindThread(FindThreadCallback cb,void * arg)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 *
FindThreadContextLocked(FindThreadCallback cb,void * arg)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 
FindThreadContextByOsIdCallback(ThreadContextBase * tctx,void * arg)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 
FindThreadContextByOsIDLocked(tid_t os_id)216 ThreadContextBase *ThreadRegistry::FindThreadContextByOsIDLocked(tid_t os_id) {
217   return FindThreadContextLocked(FindThreadContextByOsIdCallback,
218                                  (void *)os_id);
219 }
220 
SetThreadName(u32 tid,const char * name)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 
SetThreadNameByUserId(uptr user_id,const char * name)230 void ThreadRegistry::SetThreadNameByUserId(uptr user_id, const char *name) {
231   ThreadRegistryLock l(this);
232   if (const auto *tid = live_.find(user_id))
233     threads_[tid->second]->SetName(name);
234 }
235 
DetachThread(u32 tid,void * arg)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("%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(tctx->user_id);
248     tctx->SetDead();
249     QuarantinePush(tctx);
250   } else {
251     tctx->detached = true;
252   }
253 }
254 
JoinThread(u32 tid,void * arg)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("%s: Join of non-existent thread\n", SanitizerToolName);
264         return;
265       }
266       if ((destroyed = tctx->GetDestroyed())) {
267         if (tctx->user_id)
268           live_.erase(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.
FinishThread(u32 tid)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(tctx->user_id);
303     tctx->SetDead();
304     QuarantinePush(tctx);
305   }
306   tctx->SetDestroyed();
307   return prev_status;
308 }
309 
StartThread(u32 tid,tid_t os_id,ThreadType thread_type,void * arg)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, thread_type, arg);
318 }
319 
QuarantinePush(ThreadContextBase * tctx)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(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(tctx);
334 }
335 
QuarantinePop()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 
ConsumeThreadUserId(uptr user_id)344 u32 ThreadRegistry::ConsumeThreadUserId(uptr user_id) {
345   ThreadRegistryLock l(this);
346   u32 tid;
347   auto *t = live_.find(user_id);
348   CHECK(t);
349   tid = t->second;
350   live_.erase(t);
351   auto *tctx = threads_[tid];
352   CHECK_EQ(tctx->user_id, user_id);
353   tctx->user_id = 0;
354   return tid;
355 }
356 
SetThreadUserId(u32 tid,uptr user_id)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 
OnFork(u32 tid)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