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