brintos

brintos / llvm-project-archived public Read only

0
0
Text · 11.6 KiB · d726d28 Raw
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