xref: /freebsd/contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_thread_registry.cpp (revision 134e17798c9af53632b372348ab828e75e65bf46)
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 namespace __sanitizer {
17 
18 ThreadContextBase::ThreadContextBase(u32 tid)
19     : tid(tid), unique_id(0), reuse_count(), os_id(0), user_id(0),
20       status(ThreadStatusInvalid), detached(false),
21       thread_type(ThreadType::Regular), parent_tid(0), next(0) {
22   name[0] = '\0';
23   atomic_store(&thread_destroyed, 0, memory_order_release);
24 }
25 
26 ThreadContextBase::~ThreadContextBase() {
27   // ThreadContextBase should never be deleted.
28   CHECK(0);
29 }
30 
31 void ThreadContextBase::SetName(const char *new_name) {
32   name[0] = '\0';
33   if (new_name) {
34     internal_strncpy(name, new_name, sizeof(name));
35     name[sizeof(name) - 1] = '\0';
36   }
37 }
38 
39 void ThreadContextBase::SetDead() {
40   CHECK(status == ThreadStatusRunning ||
41         status == ThreadStatusFinished);
42   status = ThreadStatusDead;
43   user_id = 0;
44   OnDead();
45 }
46 
47 void ThreadContextBase::SetDestroyed() {
48   atomic_store(&thread_destroyed, 1, memory_order_release);
49 }
50 
51 bool ThreadContextBase::GetDestroyed() {
52   return !!atomic_load(&thread_destroyed, memory_order_acquire);
53 }
54 
55 void ThreadContextBase::SetJoined(void *arg) {
56   // FIXME(dvyukov): print message and continue (it's user error).
57   CHECK_EQ(false, detached);
58   CHECK_EQ(ThreadStatusFinished, status);
59   status = ThreadStatusDead;
60   user_id = 0;
61   OnJoined(arg);
62 }
63 
64 void ThreadContextBase::SetFinished() {
65   // ThreadRegistry::FinishThread calls here in ThreadStatusCreated state
66   // for a thread that never actually started.  In that case the thread
67   // should go to ThreadStatusFinished regardless of whether it was created
68   // as detached.
69   if (!detached || status == ThreadStatusCreated) status = ThreadStatusFinished;
70   OnFinished();
71 }
72 
73 void ThreadContextBase::SetStarted(tid_t _os_id, ThreadType _thread_type,
74                                    void *arg) {
75   status = ThreadStatusRunning;
76   os_id = _os_id;
77   thread_type = _thread_type;
78   OnStarted(arg);
79 }
80 
81 void ThreadContextBase::SetCreated(uptr _user_id, u64 _unique_id,
82                                    bool _detached, u32 _parent_tid, void *arg) {
83   status = ThreadStatusCreated;
84   user_id = _user_id;
85   unique_id = _unique_id;
86   detached = _detached;
87   // Parent tid makes no sense for the main thread.
88   if (tid != 0)
89     parent_tid = _parent_tid;
90   OnCreated(arg);
91 }
92 
93 void ThreadContextBase::Reset() {
94   status = ThreadStatusInvalid;
95   SetName(0);
96   atomic_store(&thread_destroyed, 0, memory_order_release);
97   OnReset();
98 }
99 
100 // ThreadRegistry implementation.
101 
102 const u32 ThreadRegistry::kUnknownTid = ~0U;
103 
104 ThreadRegistry::ThreadRegistry(ThreadContextFactory factory, u32 max_threads,
105                                u32 thread_quarantine_size, u32 max_reuse)
106     : context_factory_(factory),
107       max_threads_(max_threads),
108       thread_quarantine_size_(thread_quarantine_size),
109       max_reuse_(max_reuse),
110       mtx_(),
111       n_contexts_(0),
112       total_threads_(0),
113       alive_threads_(0),
114       max_alive_threads_(0),
115       running_threads_(0) {
116   threads_ = (ThreadContextBase **)MmapOrDie(max_threads_ * sizeof(threads_[0]),
117                                              "ThreadRegistry");
118   dead_threads_.clear();
119   invalid_threads_.clear();
120 }
121 
122 void ThreadRegistry::GetNumberOfThreads(uptr *total, uptr *running,
123                                         uptr *alive) {
124   BlockingMutexLock l(&mtx_);
125   if (total) *total = n_contexts_;
126   if (running) *running = running_threads_;
127   if (alive) *alive = alive_threads_;
128 }
129 
130 uptr ThreadRegistry::GetMaxAliveThreads() {
131   BlockingMutexLock l(&mtx_);
132   return max_alive_threads_;
133 }
134 
135 u32 ThreadRegistry::CreateThread(uptr user_id, bool detached, u32 parent_tid,
136                                  void *arg) {
137   BlockingMutexLock l(&mtx_);
138   u32 tid = kUnknownTid;
139   ThreadContextBase *tctx = QuarantinePop();
140   if (tctx) {
141     tid = tctx->tid;
142   } else if (n_contexts_ < max_threads_) {
143     // Allocate new thread context and tid.
144     tid = n_contexts_++;
145     tctx = context_factory_(tid);
146     threads_[tid] = tctx;
147   } else {
148 #if !SANITIZER_GO
149     Report("%s: Thread limit (%u threads) exceeded. Dying.\n",
150            SanitizerToolName, max_threads_);
151 #else
152     Printf("race: limit on %u simultaneously alive goroutines is exceeded,"
153         " dying\n", max_threads_);
154 #endif
155     Die();
156   }
157   CHECK_NE(tctx, 0);
158   CHECK_NE(tid, kUnknownTid);
159   CHECK_LT(tid, max_threads_);
160   CHECK_EQ(tctx->status, ThreadStatusInvalid);
161   alive_threads_++;
162   if (max_alive_threads_ < alive_threads_) {
163     max_alive_threads_++;
164     CHECK_EQ(alive_threads_, max_alive_threads_);
165   }
166   tctx->SetCreated(user_id, total_threads_++, detached,
167                    parent_tid, arg);
168   return tid;
169 }
170 
171 void ThreadRegistry::RunCallbackForEachThreadLocked(ThreadCallback cb,
172                                                     void *arg) {
173   CheckLocked();
174   for (u32 tid = 0; tid < n_contexts_; tid++) {
175     ThreadContextBase *tctx = threads_[tid];
176     if (tctx == 0)
177       continue;
178     cb(tctx, arg);
179   }
180 }
181 
182 u32 ThreadRegistry::FindThread(FindThreadCallback cb, void *arg) {
183   BlockingMutexLock l(&mtx_);
184   for (u32 tid = 0; tid < n_contexts_; tid++) {
185     ThreadContextBase *tctx = threads_[tid];
186     if (tctx != 0 && cb(tctx, arg))
187       return tctx->tid;
188   }
189   return kUnknownTid;
190 }
191 
192 ThreadContextBase *
193 ThreadRegistry::FindThreadContextLocked(FindThreadCallback cb, void *arg) {
194   CheckLocked();
195   for (u32 tid = 0; tid < n_contexts_; tid++) {
196     ThreadContextBase *tctx = threads_[tid];
197     if (tctx != 0 && cb(tctx, arg))
198       return tctx;
199   }
200   return 0;
201 }
202 
203 static bool FindThreadContextByOsIdCallback(ThreadContextBase *tctx,
204                                             void *arg) {
205   return (tctx->os_id == (uptr)arg && tctx->status != ThreadStatusInvalid &&
206       tctx->status != ThreadStatusDead);
207 }
208 
209 ThreadContextBase *ThreadRegistry::FindThreadContextByOsIDLocked(tid_t os_id) {
210   return FindThreadContextLocked(FindThreadContextByOsIdCallback,
211                                  (void *)os_id);
212 }
213 
214 void ThreadRegistry::SetThreadName(u32 tid, const char *name) {
215   BlockingMutexLock l(&mtx_);
216   CHECK_LT(tid, n_contexts_);
217   ThreadContextBase *tctx = threads_[tid];
218   CHECK_NE(tctx, 0);
219   CHECK_EQ(SANITIZER_FUCHSIA ? ThreadStatusCreated : ThreadStatusRunning,
220            tctx->status);
221   tctx->SetName(name);
222 }
223 
224 void ThreadRegistry::SetThreadNameByUserId(uptr user_id, const char *name) {
225   BlockingMutexLock l(&mtx_);
226   for (u32 tid = 0; tid < n_contexts_; tid++) {
227     ThreadContextBase *tctx = threads_[tid];
228     if (tctx != 0 && tctx->user_id == user_id &&
229         tctx->status != ThreadStatusInvalid) {
230       tctx->SetName(name);
231       return;
232     }
233   }
234 }
235 
236 void ThreadRegistry::DetachThread(u32 tid, void *arg) {
237   BlockingMutexLock l(&mtx_);
238   CHECK_LT(tid, n_contexts_);
239   ThreadContextBase *tctx = threads_[tid];
240   CHECK_NE(tctx, 0);
241   if (tctx->status == ThreadStatusInvalid) {
242     Report("%s: Detach of non-existent thread\n", SanitizerToolName);
243     return;
244   }
245   tctx->OnDetached(arg);
246   if (tctx->status == ThreadStatusFinished) {
247     tctx->SetDead();
248     QuarantinePush(tctx);
249   } else {
250     tctx->detached = true;
251   }
252 }
253 
254 void ThreadRegistry::JoinThread(u32 tid, void *arg) {
255   bool destroyed = false;
256   do {
257     {
258       BlockingMutexLock l(&mtx_);
259       CHECK_LT(tid, n_contexts_);
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         tctx->SetJoined(arg);
268         QuarantinePush(tctx);
269       }
270     }
271     if (!destroyed)
272       internal_sched_yield();
273   } while (!destroyed);
274 }
275 
276 // Normally this is called when the thread is about to exit.  If
277 // called in ThreadStatusCreated state, then this thread was never
278 // really started.  We just did CreateThread for a prospective new
279 // thread before trying to create it, and then failed to actually
280 // create it, and so never called StartThread.
281 void ThreadRegistry::FinishThread(u32 tid) {
282   BlockingMutexLock l(&mtx_);
283   CHECK_GT(alive_threads_, 0);
284   alive_threads_--;
285   CHECK_LT(tid, n_contexts_);
286   ThreadContextBase *tctx = threads_[tid];
287   CHECK_NE(tctx, 0);
288   bool dead = tctx->detached;
289   if (tctx->status == ThreadStatusRunning) {
290     CHECK_GT(running_threads_, 0);
291     running_threads_--;
292   } else {
293     // The thread never really existed.
294     CHECK_EQ(tctx->status, ThreadStatusCreated);
295     dead = true;
296   }
297   tctx->SetFinished();
298   if (dead) {
299     tctx->SetDead();
300     QuarantinePush(tctx);
301   }
302   tctx->SetDestroyed();
303 }
304 
305 void ThreadRegistry::StartThread(u32 tid, tid_t os_id, ThreadType thread_type,
306                                  void *arg) {
307   BlockingMutexLock l(&mtx_);
308   running_threads_++;
309   CHECK_LT(tid, n_contexts_);
310   ThreadContextBase *tctx = threads_[tid];
311   CHECK_NE(tctx, 0);
312   CHECK_EQ(ThreadStatusCreated, tctx->status);
313   tctx->SetStarted(os_id, thread_type, arg);
314 }
315 
316 void ThreadRegistry::QuarantinePush(ThreadContextBase *tctx) {
317   if (tctx->tid == 0)
318     return;  // Don't reuse the main thread.  It's a special snowflake.
319   dead_threads_.push_back(tctx);
320   if (dead_threads_.size() <= thread_quarantine_size_)
321     return;
322   tctx = dead_threads_.front();
323   dead_threads_.pop_front();
324   CHECK_EQ(tctx->status, ThreadStatusDead);
325   tctx->Reset();
326   tctx->reuse_count++;
327   if (max_reuse_ > 0 && tctx->reuse_count >= max_reuse_)
328     return;
329   invalid_threads_.push_back(tctx);
330 }
331 
332 ThreadContextBase *ThreadRegistry::QuarantinePop() {
333   if (invalid_threads_.size() == 0)
334     return 0;
335   ThreadContextBase *tctx = invalid_threads_.front();
336   invalid_threads_.pop_front();
337   return tctx;
338 }
339 
340 void ThreadRegistry::SetThreadUserId(u32 tid, uptr user_id) {
341   BlockingMutexLock l(&mtx_);
342   CHECK_LT(tid, n_contexts_);
343   ThreadContextBase *tctx = threads_[tid];
344   CHECK_NE(tctx, 0);
345   CHECK_NE(tctx->status, ThreadStatusInvalid);
346   CHECK_NE(tctx->status, ThreadStatusDead);
347   CHECK_EQ(tctx->user_id, 0);
348   tctx->user_id = user_id;
349 }
350 
351 }  // namespace __sanitizer
352