]>
Commit | Line | Data |
---|---|---|
ef1b3fda KS |
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 | ||
17 | ThreadContextBase::ThreadContextBase(u32 tid) | |
dee5ea7a KS |
18 | : tid(tid), unique_id(0), reuse_count(), os_id(0), user_id(0), |
19 | status(ThreadStatusInvalid), | |
5d3805fc | 20 | detached(false), workerthread(false), parent_tid(0), next(0) { |
ef1b3fda | 21 | name[0] = '\0'; |
eac97531 | 22 | atomic_store(&thread_destroyed, 0, memory_order_release); |
ef1b3fda KS |
23 | } |
24 | ||
25 | ThreadContextBase::~ThreadContextBase() { | |
26 | // ThreadContextBase should never be deleted. | |
27 | CHECK(0); | |
28 | } | |
29 | ||
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 | ||
38 | void ThreadContextBase::SetDead() { | |
39 | CHECK(status == ThreadStatusRunning || | |
40 | status == ThreadStatusFinished); | |
41 | status = ThreadStatusDead; | |
42 | user_id = 0; | |
43 | OnDead(); | |
44 | } | |
45 | ||
eac97531 ML |
46 | void ThreadContextBase::SetDestroyed() { |
47 | atomic_store(&thread_destroyed, 1, memory_order_release); | |
48 | } | |
49 | ||
50 | bool ThreadContextBase::GetDestroyed() { | |
51 | return !!atomic_load(&thread_destroyed, memory_order_acquire); | |
52 | } | |
53 | ||
ef1b3fda KS |
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 | ||
63 | void ThreadContextBase::SetFinished() { | |
5d3805fc JJ |
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; | |
ef1b3fda KS |
69 | OnFinished(); |
70 | } | |
71 | ||
5d3805fc JJ |
72 | void ThreadContextBase::SetStarted(tid_t _os_id, bool _workerthread, |
73 | void *arg) { | |
ef1b3fda KS |
74 | status = ThreadStatusRunning; |
75 | os_id = _os_id; | |
5d3805fc | 76 | workerthread = _workerthread; |
ef1b3fda KS |
77 | OnStarted(arg); |
78 | } | |
79 | ||
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 | ||
92 | void ThreadContextBase::Reset() { | |
93 | status = ThreadStatusInvalid; | |
ef1b3fda | 94 | SetName(0); |
eac97531 | 95 | atomic_store(&thread_destroyed, 0, memory_order_release); |
ef1b3fda KS |
96 | OnReset(); |
97 | } | |
98 | ||
99 | // ThreadRegistry implementation. | |
100 | ||
101 | const u32 ThreadRegistry::kUnknownTid = ~0U; | |
102 | ||
103 | ThreadRegistry::ThreadRegistry(ThreadContextFactory factory, u32 max_threads, | |
dee5ea7a | 104 | u32 thread_quarantine_size, u32 max_reuse) |
ef1b3fda KS |
105 | : context_factory_(factory), |
106 | max_threads_(max_threads), | |
107 | thread_quarantine_size_(thread_quarantine_size), | |
dee5ea7a | 108 | max_reuse_(max_reuse), |
ef1b3fda KS |
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 | ||
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 | ||
129 | uptr ThreadRegistry::GetMaxAliveThreads() { | |
130 | BlockingMutexLock l(&mtx_); | |
131 | return max_alive_threads_; | |
132 | } | |
133 | ||
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 { | |
10189819 | 147 | #if !SANITIZER_GO |
ef1b3fda KS |
148 | Report("%s: Thread limit (%u threads) exceeded. Dying.\n", |
149 | SanitizerToolName, max_threads_); | |
dee5ea7a KS |
150 | #else |
151 | Printf("race: limit on %u simultaneously alive goroutines is exceeded," | |
152 | " dying\n", max_threads_); | |
153 | #endif | |
ef1b3fda KS |
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 | ||
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 | ||
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 * | |
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 | ||
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 | ||
5d3805fc | 208 | ThreadContextBase *ThreadRegistry::FindThreadContextByOsIDLocked(tid_t os_id) { |
ef1b3fda KS |
209 | return FindThreadContextLocked(FindThreadContextByOsIdCallback, |
210 | (void *)os_id); | |
211 | } | |
212 | ||
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); | |
5d3805fc JJ |
218 | CHECK_EQ(SANITIZER_FUCHSIA ? ThreadStatusCreated : ThreadStatusRunning, |
219 | tctx->status); | |
ef1b3fda KS |
220 | tctx->SetName(name); |
221 | } | |
222 | ||
df77f0e4 KS |
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 | ||
866e32ad | 235 | void ThreadRegistry::DetachThread(u32 tid, void *arg) { |
ef1b3fda KS |
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 | } | |
866e32ad | 244 | tctx->OnDetached(arg); |
ef1b3fda KS |
245 | if (tctx->status == ThreadStatusFinished) { |
246 | tctx->SetDead(); | |
247 | QuarantinePush(tctx); | |
248 | } else { | |
249 | tctx->detached = true; | |
250 | } | |
251 | } | |
252 | ||
253 | void ThreadRegistry::JoinThread(u32 tid, void *arg) { | |
eac97531 ML |
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); | |
ef1b3fda KS |
273 | } |
274 | ||
5d3805fc JJ |
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. | |
ef1b3fda KS |
280 | void ThreadRegistry::FinishThread(u32 tid) { |
281 | BlockingMutexLock l(&mtx_); | |
282 | CHECK_GT(alive_threads_, 0); | |
283 | alive_threads_--; | |
ef1b3fda KS |
284 | CHECK_LT(tid, n_contexts_); |
285 | ThreadContextBase *tctx = threads_[tid]; | |
286 | CHECK_NE(tctx, 0); | |
5d3805fc JJ |
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 | } | |
ef1b3fda | 296 | tctx->SetFinished(); |
5d3805fc | 297 | if (dead) { |
ef1b3fda KS |
298 | tctx->SetDead(); |
299 | QuarantinePush(tctx); | |
300 | } | |
eac97531 | 301 | tctx->SetDestroyed(); |
ef1b3fda KS |
302 | } |
303 | ||
5d3805fc JJ |
304 | void ThreadRegistry::StartThread(u32 tid, tid_t os_id, bool workerthread, |
305 | void *arg) { | |
ef1b3fda KS |
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); | |
5d3805fc | 312 | tctx->SetStarted(os_id, workerthread, arg); |
ef1b3fda KS |
313 | } |
314 | ||
315 | void ThreadRegistry::QuarantinePush(ThreadContextBase *tctx) { | |
10189819 MO |
316 | if (tctx->tid == 0) |
317 | return; // Don't reuse the main thread. It's a special snowflake. | |
ef1b3fda KS |
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(); | |
dee5ea7a KS |
325 | tctx->reuse_count++; |
326 | if (max_reuse_ > 0 && tctx->reuse_count >= max_reuse_) | |
327 | return; | |
ef1b3fda KS |
328 | invalid_threads_.push_back(tctx); |
329 | } | |
330 | ||
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 |