]>
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 KS |
21 | name[0] = '\0'; |
22 | } | |
23 | ||
24 | ThreadContextBase::~ThreadContextBase() { | |
25 | // ThreadContextBase should never be deleted. | |
26 | CHECK(0); | |
27 | } | |
28 | ||
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 | ||
37 | void ThreadContextBase::SetDead() { | |
38 | CHECK(status == ThreadStatusRunning || | |
39 | status == ThreadStatusFinished); | |
40 | status = ThreadStatusDead; | |
41 | user_id = 0; | |
42 | OnDead(); | |
43 | } | |
44 | ||
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 | ||
54 | void ThreadContextBase::SetFinished() { | |
5d3805fc JJ |
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; | |
ef1b3fda KS |
60 | OnFinished(); |
61 | } | |
62 | ||
5d3805fc JJ |
63 | void ThreadContextBase::SetStarted(tid_t _os_id, bool _workerthread, |
64 | void *arg) { | |
ef1b3fda KS |
65 | status = ThreadStatusRunning; |
66 | os_id = _os_id; | |
5d3805fc | 67 | workerthread = _workerthread; |
ef1b3fda KS |
68 | OnStarted(arg); |
69 | } | |
70 | ||
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 | ||
83 | void ThreadContextBase::Reset() { | |
84 | status = ThreadStatusInvalid; | |
ef1b3fda KS |
85 | SetName(0); |
86 | OnReset(); | |
87 | } | |
88 | ||
89 | // ThreadRegistry implementation. | |
90 | ||
91 | const u32 ThreadRegistry::kUnknownTid = ~0U; | |
92 | ||
93 | ThreadRegistry::ThreadRegistry(ThreadContextFactory factory, u32 max_threads, | |
dee5ea7a | 94 | u32 thread_quarantine_size, u32 max_reuse) |
ef1b3fda KS |
95 | : context_factory_(factory), |
96 | max_threads_(max_threads), | |
97 | thread_quarantine_size_(thread_quarantine_size), | |
dee5ea7a | 98 | max_reuse_(max_reuse), |
ef1b3fda KS |
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 | ||
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 | ||
119 | uptr ThreadRegistry::GetMaxAliveThreads() { | |
120 | BlockingMutexLock l(&mtx_); | |
121 | return max_alive_threads_; | |
122 | } | |
123 | ||
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 { | |
10189819 | 137 | #if !SANITIZER_GO |
ef1b3fda KS |
138 | Report("%s: Thread limit (%u threads) exceeded. Dying.\n", |
139 | SanitizerToolName, max_threads_); | |
dee5ea7a KS |
140 | #else |
141 | Printf("race: limit on %u simultaneously alive goroutines is exceeded," | |
142 | " dying\n", max_threads_); | |
143 | #endif | |
ef1b3fda KS |
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 | ||
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 | ||
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 * | |
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 | ||
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 | ||
5d3805fc | 198 | ThreadContextBase *ThreadRegistry::FindThreadContextByOsIDLocked(tid_t os_id) { |
ef1b3fda KS |
199 | return FindThreadContextLocked(FindThreadContextByOsIdCallback, |
200 | (void *)os_id); | |
201 | } | |
202 | ||
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); | |
5d3805fc JJ |
208 | CHECK_EQ(SANITIZER_FUCHSIA ? ThreadStatusCreated : ThreadStatusRunning, |
209 | tctx->status); | |
ef1b3fda KS |
210 | tctx->SetName(name); |
211 | } | |
212 | ||
df77f0e4 KS |
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 | ||
866e32ad | 225 | void ThreadRegistry::DetachThread(u32 tid, void *arg) { |
ef1b3fda KS |
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 | } | |
866e32ad | 234 | tctx->OnDetached(arg); |
ef1b3fda KS |
235 | if (tctx->status == ThreadStatusFinished) { |
236 | tctx->SetDead(); | |
237 | QuarantinePush(tctx); | |
238 | } else { | |
239 | tctx->detached = true; | |
240 | } | |
241 | } | |
242 | ||
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 | ||
5d3805fc JJ |
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. | |
ef1b3fda KS |
261 | void ThreadRegistry::FinishThread(u32 tid) { |
262 | BlockingMutexLock l(&mtx_); | |
263 | CHECK_GT(alive_threads_, 0); | |
264 | alive_threads_--; | |
ef1b3fda KS |
265 | CHECK_LT(tid, n_contexts_); |
266 | ThreadContextBase *tctx = threads_[tid]; | |
267 | CHECK_NE(tctx, 0); | |
5d3805fc JJ |
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 | } | |
ef1b3fda | 277 | tctx->SetFinished(); |
5d3805fc | 278 | if (dead) { |
ef1b3fda KS |
279 | tctx->SetDead(); |
280 | QuarantinePush(tctx); | |
281 | } | |
282 | } | |
283 | ||
5d3805fc JJ |
284 | void ThreadRegistry::StartThread(u32 tid, tid_t os_id, bool workerthread, |
285 | void *arg) { | |
ef1b3fda KS |
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); | |
5d3805fc | 292 | tctx->SetStarted(os_id, workerthread, arg); |
ef1b3fda KS |
293 | } |
294 | ||
295 | void ThreadRegistry::QuarantinePush(ThreadContextBase *tctx) { | |
10189819 MO |
296 | if (tctx->tid == 0) |
297 | return; // Don't reuse the main thread. It's a special snowflake. | |
ef1b3fda KS |
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(); | |
dee5ea7a KS |
305 | tctx->reuse_count++; |
306 | if (max_reuse_ > 0 && tctx->reuse_count >= max_reuse_) | |
307 | return; | |
ef1b3fda KS |
308 | invalid_threads_.push_back(tctx); |
309 | } | |
310 | ||
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 |