]>
Commit | Line | Data |
---|---|---|
b667dd70 | 1 | //===-- sanitizer_thread_registry.cpp -------------------------------------===// |
ef1b3fda | 2 | // |
b667dd70 ML |
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 | |
ef1b3fda KS |
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) | |
dee5ea7a | 19 | : tid(tid), unique_id(0), reuse_count(), os_id(0), user_id(0), |
b667dd70 ML |
20 | status(ThreadStatusInvalid), detached(false), |
21 | thread_type(ThreadType::Regular), parent_tid(0), next(0) { | |
ef1b3fda | 22 | name[0] = '\0'; |
eac97531 | 23 | atomic_store(&thread_destroyed, 0, memory_order_release); |
ef1b3fda KS |
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 | ||
eac97531 ML |
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 | ||
ef1b3fda KS |
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() { | |
5d3805fc JJ |
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; | |
ef1b3fda KS |
70 | OnFinished(); |
71 | } | |
72 | ||
b667dd70 | 73 | void ThreadContextBase::SetStarted(tid_t _os_id, ThreadType _thread_type, |
5d3805fc | 74 | void *arg) { |
ef1b3fda KS |
75 | status = ThreadStatusRunning; |
76 | os_id = _os_id; | |
b667dd70 | 77 | thread_type = _thread_type; |
ef1b3fda KS |
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; | |
ef1b3fda | 95 | SetName(0); |
eac97531 | 96 | atomic_store(&thread_destroyed, 0, memory_order_release); |
ef1b3fda KS |
97 | OnReset(); |
98 | } | |
99 | ||
100 | // ThreadRegistry implementation. | |
101 | ||
102 | const u32 ThreadRegistry::kUnknownTid = ~0U; | |
103 | ||
104 | ThreadRegistry::ThreadRegistry(ThreadContextFactory factory, u32 max_threads, | |
dee5ea7a | 105 | u32 thread_quarantine_size, u32 max_reuse) |
ef1b3fda KS |
106 | : context_factory_(factory), |
107 | max_threads_(max_threads), | |
108 | thread_quarantine_size_(thread_quarantine_size), | |
dee5ea7a | 109 | max_reuse_(max_reuse), |
ef1b3fda KS |
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 { | |
10189819 | 148 | #if !SANITIZER_GO |
ef1b3fda KS |
149 | Report("%s: Thread limit (%u threads) exceeded. Dying.\n", |
150 | SanitizerToolName, max_threads_); | |
dee5ea7a KS |
151 | #else |
152 | Printf("race: limit on %u simultaneously alive goroutines is exceeded," | |
153 | " dying\n", max_threads_); | |
154 | #endif | |
ef1b3fda KS |
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 | ||
5d3805fc | 209 | ThreadContextBase *ThreadRegistry::FindThreadContextByOsIDLocked(tid_t os_id) { |
ef1b3fda KS |
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); | |
5d3805fc JJ |
219 | CHECK_EQ(SANITIZER_FUCHSIA ? ThreadStatusCreated : ThreadStatusRunning, |
220 | tctx->status); | |
ef1b3fda KS |
221 | tctx->SetName(name); |
222 | } | |
223 | ||
df77f0e4 KS |
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 | ||
866e32ad | 236 | void ThreadRegistry::DetachThread(u32 tid, void *arg) { |
ef1b3fda KS |
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 | } | |
866e32ad | 245 | tctx->OnDetached(arg); |
ef1b3fda KS |
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) { | |
eac97531 ML |
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); | |
ef1b3fda KS |
274 | } |
275 | ||
5d3805fc JJ |
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. | |
ef1b3fda KS |
281 | void ThreadRegistry::FinishThread(u32 tid) { |
282 | BlockingMutexLock l(&mtx_); | |
283 | CHECK_GT(alive_threads_, 0); | |
284 | alive_threads_--; | |
ef1b3fda KS |
285 | CHECK_LT(tid, n_contexts_); |
286 | ThreadContextBase *tctx = threads_[tid]; | |
287 | CHECK_NE(tctx, 0); | |
5d3805fc JJ |
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 | } | |
ef1b3fda | 297 | tctx->SetFinished(); |
5d3805fc | 298 | if (dead) { |
ef1b3fda KS |
299 | tctx->SetDead(); |
300 | QuarantinePush(tctx); | |
301 | } | |
eac97531 | 302 | tctx->SetDestroyed(); |
ef1b3fda KS |
303 | } |
304 | ||
b667dd70 | 305 | void ThreadRegistry::StartThread(u32 tid, tid_t os_id, ThreadType thread_type, |
5d3805fc | 306 | void *arg) { |
ef1b3fda KS |
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); | |
b667dd70 | 313 | tctx->SetStarted(os_id, thread_type, arg); |
ef1b3fda KS |
314 | } |
315 | ||
316 | void ThreadRegistry::QuarantinePush(ThreadContextBase *tctx) { | |
10189819 MO |
317 | if (tctx->tid == 0) |
318 | return; // Don't reuse the main thread. It's a special snowflake. | |
ef1b3fda KS |
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(); | |
dee5ea7a KS |
326 | tctx->reuse_count++; |
327 | if (max_reuse_ > 0 && tctx->reuse_count >= max_reuse_) | |
328 | return; | |
ef1b3fda KS |
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 | ||
b667dd70 ML |
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 | ||
ef1b3fda | 351 | } // namespace __sanitizer |