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