• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===-- sanitizer_thread_registry.cc --------------------------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This file is shared between sanitizer tools.
11 //
12 // General thread bookkeeping functionality.
13 //===----------------------------------------------------------------------===//
14 
15 #include "sanitizer_thread_registry.h"
16 
17 namespace __sanitizer {
18 
ThreadContextBase(u32 tid)19 ThreadContextBase::ThreadContextBase(u32 tid)
20     : tid(tid), unique_id(0), os_id(0), user_id(0), status(ThreadStatusInvalid),
21       detached(false), reuse_count(0), parent_tid(0), next(0) {
22   name[0] = '\0';
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 
SetJoined(void * arg)46 void ThreadContextBase::SetJoined(void *arg) {
47   // FIXME(dvyukov): print message and continue (it's user error).
48   CHECK_EQ(false, detached);
49   CHECK_EQ(ThreadStatusFinished, status);
50   status = ThreadStatusDead;
51   user_id = 0;
52   OnJoined(arg);
53 }
54 
SetFinished()55 void ThreadContextBase::SetFinished() {
56   if (!detached)
57     status = ThreadStatusFinished;
58   OnFinished();
59 }
60 
SetStarted(uptr _os_id,void * arg)61 void ThreadContextBase::SetStarted(uptr _os_id, void *arg) {
62   status = ThreadStatusRunning;
63   os_id = _os_id;
64   OnStarted(arg);
65 }
66 
SetCreated(uptr _user_id,u64 _unique_id,bool _detached,u32 _parent_tid,void * arg)67 void ThreadContextBase::SetCreated(uptr _user_id, u64 _unique_id,
68                                    bool _detached, u32 _parent_tid, void *arg) {
69   status = ThreadStatusCreated;
70   user_id = _user_id;
71   unique_id = _unique_id;
72   detached = _detached;
73   // Parent tid makes no sense for the main thread.
74   if (tid != 0)
75     parent_tid = _parent_tid;
76   OnCreated(arg);
77 }
78 
Reset()79 void ThreadContextBase::Reset() {
80   status = ThreadStatusInvalid;
81   reuse_count++;
82   SetName(0);
83   OnReset();
84 }
85 
86 // ThreadRegistry implementation.
87 
88 const u32 ThreadRegistry::kUnknownTid = ~0U;
89 
ThreadRegistry(ThreadContextFactory factory,u32 max_threads,u32 thread_quarantine_size)90 ThreadRegistry::ThreadRegistry(ThreadContextFactory factory, u32 max_threads,
91                                u32 thread_quarantine_size)
92     : context_factory_(factory),
93       max_threads_(max_threads),
94       thread_quarantine_size_(thread_quarantine_size),
95       mtx_(),
96       n_contexts_(0),
97       total_threads_(0),
98       alive_threads_(0),
99       max_alive_threads_(0),
100       running_threads_(0) {
101   threads_ = (ThreadContextBase **)MmapOrDie(max_threads_ * sizeof(threads_[0]),
102                                              "ThreadRegistry");
103   dead_threads_.clear();
104   invalid_threads_.clear();
105 }
106 
GetNumberOfThreads(uptr * total,uptr * running,uptr * alive)107 void ThreadRegistry::GetNumberOfThreads(uptr *total, uptr *running,
108                                         uptr *alive) {
109   BlockingMutexLock l(&mtx_);
110   if (total) *total = n_contexts_;
111   if (running) *running = running_threads_;
112   if (alive) *alive = alive_threads_;
113 }
114 
GetMaxAliveThreads()115 uptr ThreadRegistry::GetMaxAliveThreads() {
116   BlockingMutexLock l(&mtx_);
117   return max_alive_threads_;
118 }
119 
CreateThread(uptr user_id,bool detached,u32 parent_tid,void * arg)120 u32 ThreadRegistry::CreateThread(uptr user_id, bool detached, u32 parent_tid,
121                                  void *arg) {
122   BlockingMutexLock l(&mtx_);
123   u32 tid = kUnknownTid;
124   ThreadContextBase *tctx = QuarantinePop();
125   if (tctx) {
126     tid = tctx->tid;
127   } else if (n_contexts_ < max_threads_) {
128     // Allocate new thread context and tid.
129     tid = n_contexts_++;
130     tctx = context_factory_(tid);
131     threads_[tid] = tctx;
132   } else {
133     Report("%s: Thread limit (%u threads) exceeded. Dying.\n",
134            SanitizerToolName, max_threads_);
135     Die();
136   }
137   CHECK_NE(tctx, 0);
138   CHECK_NE(tid, kUnknownTid);
139   CHECK_LT(tid, max_threads_);
140   CHECK_EQ(tctx->status, ThreadStatusInvalid);
141   alive_threads_++;
142   if (max_alive_threads_ < alive_threads_) {
143     max_alive_threads_++;
144     CHECK_EQ(alive_threads_, max_alive_threads_);
145   }
146   tctx->SetCreated(user_id, total_threads_++, detached,
147                    parent_tid, arg);
148   return tid;
149 }
150 
RunCallbackForEachThreadLocked(ThreadCallback cb,void * arg)151 void ThreadRegistry::RunCallbackForEachThreadLocked(ThreadCallback cb,
152                                                     void *arg) {
153   CheckLocked();
154   for (u32 tid = 0; tid < n_contexts_; tid++) {
155     ThreadContextBase *tctx = threads_[tid];
156     if (tctx == 0)
157       continue;
158     cb(tctx, arg);
159   }
160 }
161 
FindThread(FindThreadCallback cb,void * arg)162 u32 ThreadRegistry::FindThread(FindThreadCallback cb, void *arg) {
163   BlockingMutexLock l(&mtx_);
164   for (u32 tid = 0; tid < n_contexts_; tid++) {
165     ThreadContextBase *tctx = threads_[tid];
166     if (tctx != 0 && cb(tctx, arg))
167       return tctx->tid;
168   }
169   return kUnknownTid;
170 }
171 
172 ThreadContextBase *
FindThreadContextLocked(FindThreadCallback cb,void * arg)173 ThreadRegistry::FindThreadContextLocked(FindThreadCallback cb, void *arg) {
174   CheckLocked();
175   for (u32 tid = 0; tid < n_contexts_; tid++) {
176     ThreadContextBase *tctx = threads_[tid];
177     if (tctx != 0 && cb(tctx, arg))
178       return tctx;
179   }
180   return 0;
181 }
182 
FindThreadContextByOsIdCallback(ThreadContextBase * tctx,void * arg)183 static bool FindThreadContextByOsIdCallback(ThreadContextBase *tctx,
184                                             void *arg) {
185   return (tctx->os_id == (uptr)arg && tctx->status != ThreadStatusInvalid &&
186       tctx->status != ThreadStatusDead);
187 }
188 
FindThreadContextByOsIDLocked(uptr os_id)189 ThreadContextBase *ThreadRegistry::FindThreadContextByOsIDLocked(uptr os_id) {
190   return FindThreadContextLocked(FindThreadContextByOsIdCallback,
191                                  (void *)os_id);
192 }
193 
SetThreadName(u32 tid,const char * name)194 void ThreadRegistry::SetThreadName(u32 tid, const char *name) {
195   BlockingMutexLock l(&mtx_);
196   CHECK_LT(tid, n_contexts_);
197   ThreadContextBase *tctx = threads_[tid];
198   CHECK_NE(tctx, 0);
199   CHECK_EQ(ThreadStatusRunning, tctx->status);
200   tctx->SetName(name);
201 }
202 
DetachThread(u32 tid)203 void ThreadRegistry::DetachThread(u32 tid) {
204   BlockingMutexLock l(&mtx_);
205   CHECK_LT(tid, n_contexts_);
206   ThreadContextBase *tctx = threads_[tid];
207   CHECK_NE(tctx, 0);
208   if (tctx->status == ThreadStatusInvalid) {
209     Report("%s: Detach of non-existent thread\n", SanitizerToolName);
210     return;
211   }
212   if (tctx->status == ThreadStatusFinished) {
213     tctx->SetDead();
214     QuarantinePush(tctx);
215   } else {
216     tctx->detached = true;
217   }
218 }
219 
JoinThread(u32 tid,void * arg)220 void ThreadRegistry::JoinThread(u32 tid, void *arg) {
221   BlockingMutexLock l(&mtx_);
222   CHECK_LT(tid, n_contexts_);
223   ThreadContextBase *tctx = threads_[tid];
224   CHECK_NE(tctx, 0);
225   if (tctx->status == ThreadStatusInvalid) {
226     Report("%s: Join of non-existent thread\n", SanitizerToolName);
227     return;
228   }
229   tctx->SetJoined(arg);
230   QuarantinePush(tctx);
231 }
232 
FinishThread(u32 tid)233 void ThreadRegistry::FinishThread(u32 tid) {
234   BlockingMutexLock l(&mtx_);
235   CHECK_GT(alive_threads_, 0);
236   alive_threads_--;
237   CHECK_GT(running_threads_, 0);
238   running_threads_--;
239   CHECK_LT(tid, n_contexts_);
240   ThreadContextBase *tctx = threads_[tid];
241   CHECK_NE(tctx, 0);
242   CHECK_EQ(ThreadStatusRunning, tctx->status);
243   tctx->SetFinished();
244   if (tctx->detached) {
245     tctx->SetDead();
246     QuarantinePush(tctx);
247   }
248 }
249 
StartThread(u32 tid,uptr os_id,void * arg)250 void ThreadRegistry::StartThread(u32 tid, uptr os_id, void *arg) {
251   BlockingMutexLock l(&mtx_);
252   running_threads_++;
253   CHECK_LT(tid, n_contexts_);
254   ThreadContextBase *tctx = threads_[tid];
255   CHECK_NE(tctx, 0);
256   CHECK_EQ(ThreadStatusCreated, tctx->status);
257   tctx->SetStarted(os_id, arg);
258 }
259 
QuarantinePush(ThreadContextBase * tctx)260 void ThreadRegistry::QuarantinePush(ThreadContextBase *tctx) {
261   dead_threads_.push_back(tctx);
262   if (dead_threads_.size() <= thread_quarantine_size_)
263     return;
264   tctx = dead_threads_.front();
265   dead_threads_.pop_front();
266   CHECK_EQ(tctx->status, ThreadStatusDead);
267   tctx->Reset();
268   invalid_threads_.push_back(tctx);
269 }
270 
QuarantinePop()271 ThreadContextBase *ThreadRegistry::QuarantinePop() {
272   if (invalid_threads_.size() == 0)
273     return 0;
274   ThreadContextBase *tctx = invalid_threads_.front();
275   invalid_threads_.pop_front();
276   return tctx;
277 }
278 
279 }  // namespace __sanitizer
280