1 //===-- sanitizer_thread_registry.cc --------------------------------------===//
3 // This file is distributed under the University of Illinois Open Source
4 // License. See LICENSE.TXT for details.
6 //===----------------------------------------------------------------------===//
8 // This file is shared between sanitizer tools.
10 // General thread bookkeeping functionality.
11 //===----------------------------------------------------------------------===//
13 #include "sanitizer_thread_registry.h"
15 namespace __sanitizer
{
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) {
22 atomic_store(&thread_destroyed
, 0, memory_order_release
);
25 ThreadContextBase::~ThreadContextBase() {
26 // ThreadContextBase should never be deleted.
30 void ThreadContextBase::SetName(const char *new_name
) {
33 internal_strncpy(name
, new_name
, sizeof(name
));
34 name
[sizeof(name
) - 1] = '\0';
38 void ThreadContextBase::SetDead() {
39 CHECK(status
== ThreadStatusRunning
||
40 status
== ThreadStatusFinished
);
41 status
= ThreadStatusDead
;
46 void ThreadContextBase::SetDestroyed() {
47 atomic_store(&thread_destroyed
, 1, memory_order_release
);
50 bool ThreadContextBase::GetDestroyed() {
51 return !!atomic_load(&thread_destroyed
, memory_order_acquire
);
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
;
63 void ThreadContextBase::SetFinished() {
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
68 if (!detached
|| status
== ThreadStatusCreated
) status
= ThreadStatusFinished
;
72 void ThreadContextBase::SetStarted(tid_t _os_id
, bool _workerthread
,
74 status
= ThreadStatusRunning
;
76 workerthread
= _workerthread
;
80 void ThreadContextBase::SetCreated(uptr _user_id
, u64 _unique_id
,
81 bool _detached
, u32 _parent_tid
, void *arg
) {
82 status
= ThreadStatusCreated
;
84 unique_id
= _unique_id
;
86 // Parent tid makes no sense for the main thread.
88 parent_tid
= _parent_tid
;
92 void ThreadContextBase::Reset() {
93 status
= ThreadStatusInvalid
;
95 atomic_store(&thread_destroyed
, 0, memory_order_release
);
99 // ThreadRegistry implementation.
101 const u32
ThreadRegistry::kUnknownTid
= ~0U;
103 ThreadRegistry::ThreadRegistry(ThreadContextFactory factory
, u32 max_threads
,
104 u32 thread_quarantine_size
, u32 max_reuse
)
105 : context_factory_(factory
),
106 max_threads_(max_threads
),
107 thread_quarantine_size_(thread_quarantine_size
),
108 max_reuse_(max_reuse
),
113 max_alive_threads_(0),
114 running_threads_(0) {
115 threads_
= (ThreadContextBase
**)MmapOrDie(max_threads_
* sizeof(threads_
[0]),
117 dead_threads_
.clear();
118 invalid_threads_
.clear();
121 void ThreadRegistry::GetNumberOfThreads(uptr
*total
, uptr
*running
,
123 BlockingMutexLock
l(&mtx_
);
124 if (total
) *total
= n_contexts_
;
125 if (running
) *running
= running_threads_
;
126 if (alive
) *alive
= alive_threads_
;
129 uptr
ThreadRegistry::GetMaxAliveThreads() {
130 BlockingMutexLock
l(&mtx_
);
131 return max_alive_threads_
;
134 u32
ThreadRegistry::CreateThread(uptr user_id
, bool detached
, u32 parent_tid
,
136 BlockingMutexLock
l(&mtx_
);
137 u32 tid
= kUnknownTid
;
138 ThreadContextBase
*tctx
= QuarantinePop();
141 } else if (n_contexts_
< max_threads_
) {
142 // Allocate new thread context and tid.
144 tctx
= context_factory_(tid
);
145 threads_
[tid
] = tctx
;
148 Report("%s: Thread limit (%u threads) exceeded. Dying.\n",
149 SanitizerToolName
, max_threads_
);
151 Printf("race: limit on %u simultaneously alive goroutines is exceeded,"
152 " dying\n", max_threads_
);
157 CHECK_NE(tid
, kUnknownTid
);
158 CHECK_LT(tid
, max_threads_
);
159 CHECK_EQ(tctx
->status
, ThreadStatusInvalid
);
161 if (max_alive_threads_
< alive_threads_
) {
162 max_alive_threads_
++;
163 CHECK_EQ(alive_threads_
, max_alive_threads_
);
165 tctx
->SetCreated(user_id
, total_threads_
++, detached
,
170 void ThreadRegistry::RunCallbackForEachThreadLocked(ThreadCallback cb
,
173 for (u32 tid
= 0; tid
< n_contexts_
; tid
++) {
174 ThreadContextBase
*tctx
= threads_
[tid
];
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
))
192 ThreadRegistry::FindThreadContextLocked(FindThreadCallback cb
, void *arg
) {
194 for (u32 tid
= 0; tid
< n_contexts_
; tid
++) {
195 ThreadContextBase
*tctx
= threads_
[tid
];
196 if (tctx
!= 0 && cb(tctx
, arg
))
202 static bool FindThreadContextByOsIdCallback(ThreadContextBase
*tctx
,
204 return (tctx
->os_id
== (uptr
)arg
&& tctx
->status
!= ThreadStatusInvalid
&&
205 tctx
->status
!= ThreadStatusDead
);
208 ThreadContextBase
*ThreadRegistry::FindThreadContextByOsIDLocked(tid_t os_id
) {
209 return FindThreadContextLocked(FindThreadContextByOsIdCallback
,
213 void ThreadRegistry::SetThreadName(u32 tid
, const char *name
) {
214 BlockingMutexLock
l(&mtx_
);
215 CHECK_LT(tid
, n_contexts_
);
216 ThreadContextBase
*tctx
= threads_
[tid
];
218 CHECK_EQ(SANITIZER_FUCHSIA
? ThreadStatusCreated
: ThreadStatusRunning
,
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
) {
235 void ThreadRegistry::DetachThread(u32 tid
, void *arg
) {
236 BlockingMutexLock
l(&mtx_
);
237 CHECK_LT(tid
, n_contexts_
);
238 ThreadContextBase
*tctx
= threads_
[tid
];
240 if (tctx
->status
== ThreadStatusInvalid
) {
241 Report("%s: Detach of non-existent thread\n", SanitizerToolName
);
244 tctx
->OnDetached(arg
);
245 if (tctx
->status
== ThreadStatusFinished
) {
247 QuarantinePush(tctx
);
249 tctx
->detached
= true;
253 void ThreadRegistry::JoinThread(u32 tid
, void *arg
) {
254 bool destroyed
= false;
257 BlockingMutexLock
l(&mtx_
);
258 CHECK_LT(tid
, n_contexts_
);
259 ThreadContextBase
*tctx
= threads_
[tid
];
261 if (tctx
->status
== ThreadStatusInvalid
) {
262 Report("%s: Join of non-existent thread\n", SanitizerToolName
);
265 if ((destroyed
= tctx
->GetDestroyed())) {
266 tctx
->SetJoined(arg
);
267 QuarantinePush(tctx
);
271 internal_sched_yield();
272 } while (!destroyed
);
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.
280 void ThreadRegistry::FinishThread(u32 tid
) {
281 BlockingMutexLock
l(&mtx_
);
282 CHECK_GT(alive_threads_
, 0);
284 CHECK_LT(tid
, n_contexts_
);
285 ThreadContextBase
*tctx
= threads_
[tid
];
287 bool dead
= tctx
->detached
;
288 if (tctx
->status
== ThreadStatusRunning
) {
289 CHECK_GT(running_threads_
, 0);
292 // The thread never really existed.
293 CHECK_EQ(tctx
->status
, ThreadStatusCreated
);
299 QuarantinePush(tctx
);
301 tctx
->SetDestroyed();
304 void ThreadRegistry::StartThread(u32 tid
, tid_t os_id
, bool workerthread
,
306 BlockingMutexLock
l(&mtx_
);
308 CHECK_LT(tid
, n_contexts_
);
309 ThreadContextBase
*tctx
= threads_
[tid
];
311 CHECK_EQ(ThreadStatusCreated
, tctx
->status
);
312 tctx
->SetStarted(os_id
, workerthread
, arg
);
315 void ThreadRegistry::QuarantinePush(ThreadContextBase
*tctx
) {
317 return; // Don't reuse the main thread. It's a special snowflake.
318 dead_threads_
.push_back(tctx
);
319 if (dead_threads_
.size() <= thread_quarantine_size_
)
321 tctx
= dead_threads_
.front();
322 dead_threads_
.pop_front();
323 CHECK_EQ(tctx
->status
, ThreadStatusDead
);
326 if (max_reuse_
> 0 && tctx
->reuse_count
>= max_reuse_
)
328 invalid_threads_
.push_back(tctx
);
331 ThreadContextBase
*ThreadRegistry::QuarantinePop() {
332 if (invalid_threads_
.size() == 0)
334 ThreadContextBase
*tctx
= invalid_threads_
.front();
335 invalid_threads_
.pop_front();
339 } // namespace __sanitizer