401 lines · cpp
1//===-- sanitizer_thread_registry.cpp -------------------------------------===//2//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-exception6//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#include "sanitizer_placement_new.h"17 18namespace __sanitizer {19 20ThreadContextBase::ThreadContextBase(u32 tid)21 : tid(tid),22 unique_id(0),23 reuse_count(),24 os_id(0),25 user_id(0),26 status(ThreadStatusInvalid),27 detached(false),28 thread_type(ThreadType::Regular),29 parent_tid(0),30 stack_id(0),31 next(0) {32 name[0] = '\0';33 atomic_store(&thread_destroyed, 0, memory_order_release);34}35 36ThreadContextBase::~ThreadContextBase() {37 // ThreadContextBase should never be deleted.38 CHECK(0);39}40 41void ThreadContextBase::SetName(const char *new_name) {42 name[0] = '\0';43 if (new_name) {44 internal_strncpy(name, new_name, sizeof(name));45 name[sizeof(name) - 1] = '\0';46 }47}48 49void ThreadContextBase::SetDead() {50 CHECK(status == ThreadStatusRunning || status == ThreadStatusFinished);51 status = ThreadStatusDead;52 user_id = 0;53 OnDead();54}55 56void ThreadContextBase::SetDestroyed() {57 atomic_store(&thread_destroyed, 1, memory_order_release);58}59 60bool ThreadContextBase::GetDestroyed() {61 return !!atomic_load(&thread_destroyed, memory_order_acquire);62}63 64void ThreadContextBase::SetJoined(void *arg) {65 // FIXME(dvyukov): print message and continue (it's user error).66 CHECK_EQ(false, detached);67 CHECK_EQ(ThreadStatusFinished, status);68 status = ThreadStatusDead;69 user_id = 0;70 OnJoined(arg);71}72 73void ThreadContextBase::SetFinished() {74 // ThreadRegistry::FinishThread calls here in ThreadStatusCreated state75 // for a thread that never actually started. In that case the thread76 // should go to ThreadStatusFinished regardless of whether it was created77 // as detached.78 if (!detached || status == ThreadStatusCreated)79 status = ThreadStatusFinished;80 OnFinished();81}82 83void ThreadContextBase::SetStarted(ThreadID _os_id, ThreadType _thread_type,84 void *arg) {85 status = ThreadStatusRunning;86 os_id = _os_id;87 thread_type = _thread_type;88 OnStarted(arg);89}90 91void ThreadContextBase::SetCreated(uptr _user_id, u64 _unique_id,92 bool _detached, u32 _parent_tid,93 u32 _stack_tid, void *arg) {94 status = ThreadStatusCreated;95 user_id = _user_id;96 unique_id = _unique_id;97 detached = _detached;98 // Parent tid makes no sense for the main thread.99 if (tid != kMainTid) {100 parent_tid = _parent_tid;101 stack_id = _stack_tid;102 }103 OnCreated(arg);104}105 106void ThreadContextBase::Reset() {107 status = ThreadStatusInvalid;108 SetName(0);109 atomic_store(&thread_destroyed, 0, memory_order_release);110 OnReset();111}112 113// ThreadRegistry implementation.114 115ThreadRegistry::ThreadRegistry(ThreadContextFactory factory)116 : ThreadRegistry(factory, UINT32_MAX, UINT32_MAX, 0) {}117 118ThreadRegistry::ThreadRegistry(ThreadContextFactory factory, u32 max_threads,119 u32 thread_quarantine_size, u32 max_reuse)120 : context_factory_(factory),121 max_threads_(max_threads),122 thread_quarantine_size_(thread_quarantine_size),123 max_reuse_(max_reuse),124 mtx_(MutexThreadRegistry),125 total_threads_(0),126 alive_threads_(0),127 max_alive_threads_(0),128 running_threads_(0) {129 dead_threads_.clear();130 invalid_threads_.clear();131}132 133void ThreadRegistry::GetNumberOfThreads(uptr *total, uptr *running,134 uptr *alive) {135 ThreadRegistryLock l(this);136 if (total)137 *total = threads_.size();138 if (running)139 *running = running_threads_;140 if (alive)141 *alive = alive_threads_;142}143 144uptr ThreadRegistry::GetMaxAliveThreads() {145 ThreadRegistryLock l(this);146 return max_alive_threads_;147}148 149u32 ThreadRegistry::CreateThread(uptr user_id, bool detached, u32 parent_tid,150 u32 stack_tid, void *arg) {151 ThreadRegistryLock l(this);152 u32 tid = kInvalidTid;153 ThreadContextBase *tctx = QuarantinePop();154 if (tctx) {155 tid = tctx->tid;156 } else if (threads_.size() < max_threads_) {157 // Allocate new thread context and tid.158 tid = threads_.size();159 tctx = context_factory_(tid);160 threads_.push_back(tctx);161 } else {162#if !SANITIZER_GO163 Report("%s: Thread limit (%u threads) exceeded. Dying.\n",164 SanitizerToolName, max_threads_);165#else166 Printf(167 "race: limit on %u simultaneously alive goroutines is exceeded,"168 " dying\n",169 max_threads_);170#endif171 Die();172 }173 CHECK_NE(tctx, 0);174 CHECK_NE(tid, kInvalidTid);175 CHECK_LT(tid, max_threads_);176 CHECK_EQ(tctx->status, ThreadStatusInvalid);177 alive_threads_++;178 if (max_alive_threads_ < alive_threads_) {179 max_alive_threads_++;180 CHECK_EQ(alive_threads_, max_alive_threads_);181 }182 if (user_id) {183 // Ensure that user_id is unique. If it's not the case we are screwed.184 // Ignoring this situation may lead to very hard to debug false185 // positives later (e.g. if we join a wrong thread).186 CHECK(live_.try_emplace(user_id, tid).second);187 }188 tctx->SetCreated(user_id, total_threads_++, detached, parent_tid, stack_tid,189 arg);190 return tid;191}192 193void ThreadRegistry::RunCallbackForEachThreadLocked(ThreadCallback cb,194 void *arg) {195 CheckLocked();196 for (u32 tid = 0; tid < threads_.size(); tid++) {197 ThreadContextBase *tctx = threads_[tid];198 if (tctx == 0)199 continue;200 cb(tctx, arg);201 }202}203 204u32 ThreadRegistry::FindThread(FindThreadCallback cb, void *arg) {205 ThreadRegistryLock l(this);206 for (u32 tid = 0; tid < threads_.size(); tid++) {207 ThreadContextBase *tctx = threads_[tid];208 if (tctx != 0 && cb(tctx, arg))209 return tctx->tid;210 }211 return kInvalidTid;212}213 214ThreadContextBase *ThreadRegistry::FindThreadContextLocked(215 FindThreadCallback cb, void *arg) {216 CheckLocked();217 for (u32 tid = 0; tid < threads_.size(); tid++) {218 ThreadContextBase *tctx = threads_[tid];219 if (tctx != 0 && cb(tctx, arg))220 return tctx;221 }222 return 0;223}224 225static bool FindThreadContextByOsIdCallback(ThreadContextBase *tctx,226 void *arg) {227 return (tctx->os_id == (uptr)arg && tctx->status != ThreadStatusInvalid &&228 tctx->status != ThreadStatusDead);229}230 231ThreadContextBase *ThreadRegistry::FindThreadContextByOsIDLocked(232 ThreadID os_id) {233 return FindThreadContextLocked(FindThreadContextByOsIdCallback,234 (void *)os_id);235}236 237void ThreadRegistry::SetThreadName(u32 tid, const char *name) {238 ThreadRegistryLock l(this);239 ThreadContextBase *tctx = threads_[tid];240 CHECK_NE(tctx, 0);241 CHECK_EQ(SANITIZER_FUCHSIA ? ThreadStatusCreated : ThreadStatusRunning,242 tctx->status);243 tctx->SetName(name);244}245 246void ThreadRegistry::SetThreadNameByUserId(uptr user_id, const char *name) {247 ThreadRegistryLock l(this);248 if (const auto *tid = live_.find(user_id))249 threads_[tid->second]->SetName(name);250}251 252void ThreadRegistry::DetachThread(u32 tid, void *arg) {253 ThreadRegistryLock l(this);254 ThreadContextBase *tctx = threads_[tid];255 CHECK_NE(tctx, 0);256 if (tctx->status == ThreadStatusInvalid) {257 Report("%s: Detach of non-existent thread\n", SanitizerToolName);258 return;259 }260 tctx->OnDetached(arg);261 if (tctx->status == ThreadStatusFinished) {262 if (tctx->user_id)263 live_.erase(tctx->user_id);264 tctx->SetDead();265 QuarantinePush(tctx);266 } else {267 tctx->detached = true;268 }269}270 271void ThreadRegistry::JoinThread(u32 tid, void *arg) {272 bool destroyed = false;273 do {274 {275 ThreadRegistryLock l(this);276 ThreadContextBase *tctx = threads_[tid];277 CHECK_NE(tctx, 0);278 if (tctx->status == ThreadStatusInvalid) {279 Report("%s: Join of non-existent thread\n", SanitizerToolName);280 return;281 }282 if ((destroyed = tctx->GetDestroyed())) {283 if (tctx->user_id)284 live_.erase(tctx->user_id);285 tctx->SetJoined(arg);286 QuarantinePush(tctx);287 }288 }289 if (!destroyed)290 internal_sched_yield();291 } while (!destroyed);292}293 294// Normally this is called when the thread is about to exit. If295// called in ThreadStatusCreated state, then this thread was never296// really started. We just did CreateThread for a prospective new297// thread before trying to create it, and then failed to actually298// create it, and so never called StartThread.299ThreadStatus ThreadRegistry::FinishThread(u32 tid) {300 ThreadRegistryLock l(this);301 CHECK_GT(alive_threads_, 0);302 alive_threads_--;303 ThreadContextBase *tctx = threads_[tid];304 CHECK_NE(tctx, 0);305 bool dead = tctx->detached;306 ThreadStatus prev_status = tctx->status;307 if (tctx->status == ThreadStatusRunning) {308 CHECK_GT(running_threads_, 0);309 running_threads_--;310 } else {311 // The thread never really existed.312 CHECK_EQ(tctx->status, ThreadStatusCreated);313 dead = true;314 }315 tctx->SetFinished();316 if (dead) {317 if (tctx->user_id)318 live_.erase(tctx->user_id);319 tctx->SetDead();320 QuarantinePush(tctx);321 }322 tctx->SetDestroyed();323 return prev_status;324}325 326void ThreadRegistry::StartThread(u32 tid, ThreadID os_id,327 ThreadType thread_type, void *arg) {328 ThreadRegistryLock l(this);329 running_threads_++;330 ThreadContextBase *tctx = threads_[tid];331 CHECK_NE(tctx, 0);332 CHECK_EQ(ThreadStatusCreated, tctx->status);333 tctx->SetStarted(os_id, thread_type, arg);334}335 336void ThreadRegistry::QuarantinePush(ThreadContextBase *tctx) {337 if (tctx->tid == 0)338 return; // Don't reuse the main thread. It's a special snowflake.339 dead_threads_.push_back(tctx);340 if (dead_threads_.size() <= thread_quarantine_size_)341 return;342 tctx = dead_threads_.front();343 dead_threads_.pop_front();344 CHECK_EQ(tctx->status, ThreadStatusDead);345 tctx->Reset();346 tctx->reuse_count++;347 if (max_reuse_ > 0 && tctx->reuse_count >= max_reuse_)348 return;349 invalid_threads_.push_back(tctx);350}351 352ThreadContextBase *ThreadRegistry::QuarantinePop() {353 if (invalid_threads_.size() == 0)354 return nullptr;355 ThreadContextBase *tctx = invalid_threads_.front();356 invalid_threads_.pop_front();357 return tctx;358}359 360u32 ThreadRegistry::ConsumeThreadUserId(uptr user_id) {361 ThreadRegistryLock l(this);362 u32 tid;363 auto *t = live_.find(user_id);364 CHECK(t);365 tid = t->second;366 live_.erase(t);367 auto *tctx = threads_[tid];368 CHECK_EQ(tctx->user_id, user_id);369 tctx->user_id = 0;370 return tid;371}372 373void ThreadRegistry::SetThreadUserId(u32 tid, uptr user_id) {374 ThreadRegistryLock l(this);375 ThreadContextBase *tctx = threads_[tid];376 CHECK_NE(tctx, 0);377 CHECK_NE(tctx->status, ThreadStatusInvalid);378 CHECK_NE(tctx->status, ThreadStatusDead);379 CHECK_EQ(tctx->user_id, 0);380 tctx->user_id = user_id;381 CHECK(live_.try_emplace(user_id, tctx->tid).second);382}383 384u32 ThreadRegistry::OnFork(u32 tid) {385 ThreadRegistryLock l(this);386 // We only purge user_id (pthread_t) of live threads because387 // they cause CHECK failures if new threads with matching pthread_t388 // created after fork.389 // Potentially we could purge more info (ThreadContextBase themselves),390 // but it's hard to test and easy to introduce new issues by doing this.391 for (auto *tctx : threads_) {392 if (tctx->tid == tid || !tctx->user_id)393 continue;394 CHECK(live_.erase(tctx->user_id));395 tctx->user_id = 0;396 }397 return alive_threads_;398}399 400} // namespace __sanitizer401