[compiler-rt] r177074 - [Sanitizer] Add generic ThreadRegistry class for sanitizer runtimes. This class holds basic thread bookkeeping logic and allows specific sanitizer runtimes to create thread contexts and mark threads as created/running/joined etc. The class is based on the way we currently store thread contexts in TSan.

Alexey Samsonov samsonov at google.com
Thu Mar 14 06:54:30 PDT 2013


Author: samsonov
Date: Thu Mar 14 08:54:30 2013
New Revision: 177074

URL: http://llvm.org/viewvc/llvm-project?rev=177074&view=rev
Log:
[Sanitizer] Add generic ThreadRegistry class for sanitizer runtimes. This class holds basic thread bookkeeping logic and allows specific sanitizer runtimes to create thread contexts and mark threads as created/running/joined etc. The class is based on the way we currently store thread contexts in TSan.

Added:
    compiler-rt/trunk/lib/sanitizer_common/sanitizer_thread_registry.cc
    compiler-rt/trunk/lib/sanitizer_common/sanitizer_thread_registry.h
    compiler-rt/trunk/lib/sanitizer_common/tests/sanitizer_thread_registry_test.cc
Modified:
    compiler-rt/trunk/lib/sanitizer_common/CMakeLists.txt
    compiler-rt/trunk/lib/sanitizer_common/tests/CMakeLists.txt

Modified: compiler-rt/trunk/lib/sanitizer_common/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/CMakeLists.txt?rev=177074&r1=177073&r2=177074&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/CMakeLists.txt (original)
+++ compiler-rt/trunk/lib/sanitizer_common/CMakeLists.txt Thu Mar 14 08:54:30 2013
@@ -18,6 +18,7 @@ set(SANITIZER_SOURCES
   sanitizer_symbolizer_linux.cc
   sanitizer_symbolizer_mac.cc
   sanitizer_symbolizer_win.cc
+  sanitizer_thread_registry.cc
   sanitizer_win.cc
   )
 
@@ -47,6 +48,7 @@ set(SANITIZER_HEADERS
   sanitizer_stackdepot.h
   sanitizer_stacktrace.h
   sanitizer_symbolizer.h
+  sanitizer_thread_registry.h
   )
 
 set(SANITIZER_CFLAGS

Added: compiler-rt/trunk/lib/sanitizer_common/sanitizer_thread_registry.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_thread_registry.cc?rev=177074&view=auto
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_thread_registry.cc (added)
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_thread_registry.cc Thu Mar 14 08:54:30 2013
@@ -0,0 +1,248 @@
+//===-- sanitizer_thread_registry.cc --------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between sanitizer tools.
+//
+// General thread bookkeeping functionality.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer_thread_registry.h"
+
+namespace __sanitizer {
+
+ThreadContextBase::ThreadContextBase(u32 tid)
+    : tid(tid), unique_id(0), os_id(0), user_id(0), status(ThreadStatusInvalid),
+      detached(false), reuse_count(0), parent_tid(0), next(0) {
+  name[0] = '\0';
+}
+
+void ThreadContextBase::SetName(const char *new_name) {
+  name[0] = '\0';
+  if (new_name) {
+    internal_strncpy(name, new_name, sizeof(name));
+    name[sizeof(name) - 1] = '\0';
+  }
+}
+
+void ThreadContextBase::SetDead() {
+  CHECK(status == ThreadStatusRunning ||
+        status == ThreadStatusFinished);
+  status = ThreadStatusDead;
+  user_id = 0;
+  OnDead();
+}
+
+void ThreadContextBase::SetJoined(void *arg) {
+  // FIXME(dvyukov): print message and continue (it's user error).
+  CHECK_EQ(false, detached);
+  CHECK_EQ(ThreadStatusFinished, status);
+  status = ThreadStatusDead;
+  user_id = 0;
+  OnJoined(arg);
+}
+
+void ThreadContextBase::SetFinished() {
+  if (!detached)
+    status = ThreadStatusFinished;
+  OnFinished();
+}
+
+void ThreadContextBase::SetStarted(uptr _os_id, void *arg) {
+  status = ThreadStatusRunning;
+  os_id = _os_id;
+  OnStarted(arg);
+}
+
+void ThreadContextBase::SetCreated(uptr _user_id, u64 _unique_id,
+                                   bool _detached, u32 _parent_tid, void *arg) {
+  status = ThreadStatusCreated;
+  user_id = _user_id;
+  unique_id = _unique_id;
+  detached = _detached;
+  // Parent tid makes no sense for the main thread.
+  if (tid != 0)
+    parent_tid = _parent_tid;
+  OnCreated(arg);
+}
+
+void ThreadContextBase::Reset(void *arg) {
+  status = ThreadStatusInvalid;
+  reuse_count++;
+  SetName(0);
+  OnReset(arg);
+}
+
+// ThreadRegistry implementation.
+
+ThreadRegistry::ThreadRegistry(ThreadContextFactory factory, u32 max_threads,
+                               u32 thread_quarantine_size)
+    : context_factory_(factory),
+      max_threads_(max_threads),
+      thread_quarantine_size_(thread_quarantine_size),
+      mtx_(),
+      n_contexts_(0),
+      total_threads_(0),
+      alive_threads_(0),
+      max_alive_threads_(0),
+      running_threads_(0) {
+  threads_ = (ThreadContextBase **)MmapOrDie(max_threads_ * sizeof(threads_[0]),
+                                             "ThreadRegistry");
+  dead_threads_.clear();
+}
+
+void ThreadRegistry::GetNumberOfThreads(uptr *total, uptr *running,
+                                        uptr *alive) {
+  BlockingMutexLock l(&mtx_);
+  if (total) *total = n_contexts_;
+  if (running) *running = running_threads_;
+  if (alive) *alive = alive_threads_;
+}
+
+uptr ThreadRegistry::GetMaxAliveThreads() {
+  BlockingMutexLock l(&mtx_);
+  return max_alive_threads_;
+}
+
+u32 ThreadRegistry::CreateThread(uptr user_id, bool detached, u32 parent_tid,
+                                 void *arg) {
+  BlockingMutexLock l(&mtx_);
+  u32 tid = kUnknownTid;
+  ThreadContextBase *tctx = 0;
+  if (dead_threads_.size() > thread_quarantine_size_ ||
+      n_contexts_ >= max_threads_) {
+    // Reusing old thread descriptor and tid.
+    if (dead_threads_.size() == 0) {
+      Report("%s: Thread limit (%u threads) exceeded. Dying.\n",
+             SanitizerToolName, max_threads_);
+      Die();
+    }
+    tctx = dead_threads_.front();
+    dead_threads_.pop_front();
+    CHECK_EQ(ThreadStatusDead, tctx->status);
+    tctx->Reset(arg);
+    tid = tctx->tid;
+  } else {
+    // Allocate new thread context and tid.
+    tid = n_contexts_++;
+    tctx = context_factory_(tid);
+    threads_[tid] = tctx;
+  }
+  CHECK_NE(tctx, 0);
+  CHECK_NE(tid, kUnknownTid);
+  CHECK_LT(tid, max_threads_);
+  CHECK_EQ(tctx->status, ThreadStatusInvalid);
+  alive_threads_++;
+  if (max_alive_threads_ < alive_threads_) {
+    max_alive_threads_++;
+    CHECK_EQ(alive_threads_, max_alive_threads_);
+  }
+  tctx->SetCreated(user_id, total_threads_++, detached,
+                   parent_tid, arg);
+  return tid;
+}
+
+void ThreadRegistry::RunCallbackForEachThreadLocked(ThreadCallback cb,
+                                                    void *arg) {
+  CheckLocked();
+  for (u32 tid = 0; tid < n_contexts_; tid++) {
+    ThreadContextBase *tctx = threads_[tid];
+    if (tctx == 0)
+      continue;
+    cb(tctx, arg);
+  }
+}
+
+u32 ThreadRegistry::FindThread(FindThreadCallback cb, void *arg) {
+  BlockingMutexLock l(&mtx_);
+  for (u32 tid = 0; tid < n_contexts_; tid++) {
+    ThreadContextBase *tctx = threads_[tid];
+    if (tctx != 0 && cb(tctx, arg))
+      return tctx->tid;
+  }
+  return kUnknownTid;
+}
+
+ThreadContextBase *
+ThreadRegistry::FindThreadContextLocked(FindThreadCallback cb, void *arg) {
+  CheckLocked();
+  for (u32 tid = 0; tid < n_contexts_; tid++) {
+    ThreadContextBase *tctx = threads_[tid];
+    if (tctx != 0 && cb(tctx, arg))
+      return tctx;
+  }
+  return 0;
+}
+
+void ThreadRegistry::SetThreadName(u32 tid, const char *name) {
+  BlockingMutexLock l(&mtx_);
+  CHECK_LT(tid, n_contexts_);
+  ThreadContextBase *tctx = threads_[tid];
+  CHECK_NE(tctx, 0);
+  CHECK_EQ(ThreadStatusRunning, tctx->status);
+  tctx->SetName(name);
+}
+
+void ThreadRegistry::DetachThread(u32 tid) {
+  BlockingMutexLock l(&mtx_);
+  CHECK_LT(tid, n_contexts_);
+  ThreadContextBase *tctx = threads_[tid];
+  CHECK_NE(tctx, 0);
+  if (tctx->status == ThreadStatusInvalid) {
+    Report("%s: Detach of non-existent thread\n", SanitizerToolName);
+    return;
+  }
+  if (tctx->status == ThreadStatusFinished) {
+    tctx->SetDead();
+    dead_threads_.push_back(tctx);
+  } else {
+    tctx->detached = true;
+  }
+}
+
+void ThreadRegistry::JoinThread(u32 tid, void *arg) {
+  BlockingMutexLock l(&mtx_);
+  CHECK_LT(tid, n_contexts_);
+  ThreadContextBase *tctx = threads_[tid];
+  CHECK_NE(tctx, 0);
+  if (tctx->status == ThreadStatusInvalid) {
+    Report("%s: Join of non-existent thread\n", SanitizerToolName);
+    return;
+  }
+  tctx->SetJoined(arg);
+  dead_threads_.push_back(tctx);
+}
+
+void ThreadRegistry::FinishThread(u32 tid) {
+  BlockingMutexLock l(&mtx_);
+  CHECK_GT(alive_threads_, 0);
+  alive_threads_--;
+  CHECK_GT(running_threads_, 0);
+  running_threads_--;
+  CHECK_LT(tid, n_contexts_);
+  ThreadContextBase *tctx = threads_[tid];
+  CHECK_NE(tctx, 0);
+  CHECK_EQ(ThreadStatusRunning, tctx->status);
+  tctx->SetFinished();
+  if (tctx->detached) {
+    tctx->SetDead();
+    dead_threads_.push_back(tctx);
+  }
+}
+
+void ThreadRegistry::StartThread(u32 tid, uptr os_id, void *arg) {
+  BlockingMutexLock l(&mtx_);
+  running_threads_++;
+  CHECK_LT(tid, n_contexts_);
+  ThreadContextBase *tctx = threads_[tid];
+  CHECK_NE(tctx, 0);
+  CHECK_EQ(ThreadStatusCreated, tctx->status);
+  tctx->SetStarted(os_id, arg);
+}
+
+}  // namespace __sanitizer

Added: compiler-rt/trunk/lib/sanitizer_common/sanitizer_thread_registry.h
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/sanitizer_thread_registry.h?rev=177074&view=auto
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/sanitizer_thread_registry.h (added)
+++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_thread_registry.h Thu Mar 14 08:54:30 2013
@@ -0,0 +1,138 @@
+//===-- sanitizer_thread_registry.h -----------------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is shared between sanitizer tools.
+//
+// General thread bookkeeping functionality.
+//===----------------------------------------------------------------------===//
+
+#ifndef SANITIZER_THREAD_REGISTRY_H
+#define SANITIZER_THREAD_REGISTRY_H
+
+#include "sanitizer_common.h"
+#include "sanitizer_list.h"
+#include "sanitizer_mutex.h"
+
+namespace __sanitizer {
+
+enum ThreadStatus {
+  ThreadStatusInvalid,   // Non-existent thread, data is invalid.
+  ThreadStatusCreated,   // Created but not yet running.
+  ThreadStatusRunning,   // The thread is currently running.
+  ThreadStatusFinished,  // Joinable thread is finished but not yet joined.
+  ThreadStatusDead       // Joined, but some info is still available.
+};
+
+// Generic thread context. Specific sanitizer tools may inherit from it.
+// If thread is dead, context may optionally be reused for a new thread.
+class ThreadContextBase {
+ public:
+  explicit ThreadContextBase(u32 tid);
+
+  const u32 tid;  // Thread ID. Main thread should have tid = 0.
+  u64 unique_id;  // Unique thread ID.
+  uptr os_id;     // PID (used for reporting).
+  uptr user_id;   // Some opaque user thread id (e.g. pthread_t).
+  char name[64];  // As annotated by user.
+
+  ThreadStatus status;
+  bool detached;
+  int reuse_count;
+
+  u32 parent_tid;
+  ThreadContextBase *next;  // For storing thread contexts in a list.
+
+  void SetName(const char *new_name);
+
+  void SetDead();
+  void SetJoined(void *arg);
+  void SetFinished();
+  void SetStarted(uptr _os_id, void *arg);
+  void SetCreated(uptr _user_id, u64 _unique_id, bool _detached,
+                  u32 _parent_tid, void *arg);
+  void Reset(void *arg);
+
+  // The following methods may be overriden by subclasses.
+  // Some of them take opaque arg that may be optionally be used
+  // by subclasses.
+  virtual void OnDead() {}
+  virtual void OnJoined(void *arg) {}
+  virtual void OnFinished() {}
+  virtual void OnStarted(void *arg) {}
+  virtual void OnCreated(void *arg) {}
+  virtual void OnReset(void *arg) {}
+};
+
+typedef ThreadContextBase* (*ThreadContextFactory)(u32 tid);
+
+class ThreadRegistry {
+ private:
+  const ThreadContextFactory context_factory_;
+  const u32 max_threads_;
+  const u32 thread_quarantine_size_;
+
+  static const u32 kUnknownTid = -1U;
+  BlockingMutex mtx_;
+
+  u32 n_contexts_;      // Number of created thread contexts,
+                        // at most max_threads_.
+  u64 total_threads_;   // Total number of created threads. May be greater than
+                        // max_threads_ if contexts were reused.
+  uptr alive_threads_;  // Created or running.
+  uptr max_alive_threads_;
+  uptr running_threads_;
+
+  ThreadContextBase **threads_;  // Array of thread contexts is leaked.
+  IntrusiveList<ThreadContextBase> dead_threads_;
+
+ public:
+  ThreadRegistry(ThreadContextFactory factory, u32 max_threads,
+                 u32 thread_quarantine_size);
+  void GetNumberOfThreads(uptr *total = 0, uptr *running = 0, uptr *alive = 0);
+  uptr GetMaxAliveThreads();
+
+  void Lock() { mtx_.Lock(); }
+  void CheckLocked() { mtx_.CheckLocked(); }
+  void Unlock() { mtx_.Unlock(); }
+
+  // Should be guarded by ThreadRegistryLock.
+  ThreadContextBase *GetThreadLocked(u32 tid) {
+    DCHECK_LT(tid, n_contexts_);
+    return threads_[tid];
+  }
+
+  u32 CreateThread(uptr user_id, bool detached, u32 parent_tid, void *arg);
+
+  typedef void (*ThreadCallback)(ThreadContextBase *tctx, void *arg);
+  // Invokes callback with a specified arg for each thread context.
+  // Should be guarded by ThreadRegistryLock.
+  void RunCallbackForEachThreadLocked(ThreadCallback cb, void *arg);
+
+  typedef bool (*FindThreadCallback)(ThreadContextBase *tctx, void *arg);
+  // Finds a thread using the provided callback. Returns kUnknownTid if no
+  // thread is found.
+  u32 FindThread(FindThreadCallback cb, void *arg);
+  // Should be guarded by ThreadRegistryLock. Returns 0 if no thread
+  // is found.
+  ThreadContextBase *FindThreadContextLocked(FindThreadCallback cb,
+                                             void *arg);
+
+  void SetThreadName(u32 tid, const char *name);
+  void DetachThread(u32 tid);
+  void JoinThread(u32 tid, void *arg);
+  void FinishThread(u32 tid);
+  void StartThread(u32 tid, uptr os_id, void *arg);
+};
+
+typedef GenericScopedLock<ThreadRegistry> ThreadRegistryLock;
+
+}  // namespace __sanitizer
+
+#endif  // SANITIZER_THREAD_REGISTRY_H
+

Modified: compiler-rt/trunk/lib/sanitizer_common/tests/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/tests/CMakeLists.txt?rev=177074&r1=177073&r2=177074&view=diff
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/tests/CMakeLists.txt (original)
+++ compiler-rt/trunk/lib/sanitizer_common/tests/CMakeLists.txt Thu Mar 14 08:54:30 2013
@@ -13,6 +13,7 @@ set(SANITIZER_UNITTESTS
   sanitizer_stackdepot_test.cc
   sanitizer_stacktrace_test.cc
   sanitizer_test_main.cc
+  sanitizer_thread_registry_test.cc
   )
 
 set(SANITIZER_TEST_HEADERS)

Added: compiler-rt/trunk/lib/sanitizer_common/tests/sanitizer_thread_registry_test.cc
URL: http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/sanitizer_common/tests/sanitizer_thread_registry_test.cc?rev=177074&view=auto
==============================================================================
--- compiler-rt/trunk/lib/sanitizer_common/tests/sanitizer_thread_registry_test.cc (added)
+++ compiler-rt/trunk/lib/sanitizer_common/tests/sanitizer_thread_registry_test.cc Thu Mar 14 08:54:30 2013
@@ -0,0 +1,228 @@
+//===-- sanitizer_thread_registry_test.cc ---------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of shared sanitizer runtime.
+//
+//===----------------------------------------------------------------------===//
+#include "sanitizer_common/sanitizer_thread_registry.h"
+#include "gtest/gtest.h"
+
+#include <vector>
+
+namespace __sanitizer {
+
+static BlockingMutex tctx_allocator_lock(LINKER_INITIALIZED);
+static LowLevelAllocator tctx_allocator;
+
+template<typename TCTX>
+static ThreadContextBase *GetThreadContext(u32 tid) {
+  BlockingMutexLock l(&tctx_allocator_lock);
+  void *mem = tctx_allocator.Allocate(sizeof(TCTX));
+  return new(mem) TCTX(tid);
+}
+
+static const u32 kMaxRegistryThreads = 1000;
+static const u32 kRegistryQuarantine = 2;
+
+static void CheckThreadQuantity(ThreadRegistry *registry, uptr exp_total,
+                                uptr exp_running, uptr exp_alive) {
+  uptr total, running, alive;
+  registry->GetNumberOfThreads(&total, &running, &alive);
+  EXPECT_EQ(exp_total, total);
+  EXPECT_EQ(exp_running, running);
+  EXPECT_EQ(exp_alive, alive);
+}
+
+static bool is_detached(int tid) {
+  return (tid % 2 == 0);
+}
+
+static uptr get_uid(int tid) {
+  return tid * 2;
+}
+
+static bool HasName(ThreadContextBase *tctx, void *arg) {
+  char *name = (char*)arg;
+  return (tctx->name && 0 == internal_strcmp(tctx->name, name));
+}
+
+static bool HasUid(ThreadContextBase *tctx, void *arg) {
+  uptr uid = (uptr)arg;
+  return (tctx->user_id == uid);
+}
+
+static void MarkUidAsPresent(ThreadContextBase *tctx, void *arg) {
+  bool *arr = (bool*)arg;
+  arr[tctx->tid] = true;
+}
+
+static void TestRegistry(ThreadRegistry *registry, bool has_quarantine) {
+  // Create and start a main thread.
+  EXPECT_EQ(0, registry->CreateThread(get_uid(0), true, -1, 0));
+  registry->StartThread(0, 0, 0);
+  // Create a bunch of threads.
+  for (int i = 1; i <= 10; i++) {
+    EXPECT_EQ(i, registry->CreateThread(get_uid(i), is_detached(i), 0, 0));
+  }
+  CheckThreadQuantity(registry, 11, 1, 11);
+  // Start some of them.
+  for (int i = 1; i <= 5; i++) {
+    registry->StartThread(i, 0, 0);
+  }
+  CheckThreadQuantity(registry, 11, 6, 11);
+  // Finish, create and start more threads.
+  for (int i = 1; i <= 5; i++) {
+    registry->FinishThread(i);
+    if (!is_detached(i))
+      registry->JoinThread(i, 0);
+  }
+  for (int i = 6; i <= 10; i++) {
+    registry->StartThread(i, 0, 0);
+  }
+  std::vector<int> new_tids;
+  for (int i = 11; i <= 15; i++) {
+    new_tids.push_back(
+        registry->CreateThread(get_uid(i), is_detached(i), 0, 0));
+  }
+  ASSERT_LE(kRegistryQuarantine, 5);
+  int exp_total = 16 - (has_quarantine ? 5 - kRegistryQuarantine  : 0);
+  CheckThreadQuantity(registry, exp_total, 6, 11);
+  // Test SetThreadName and FindThread.
+  registry->SetThreadName(6, "six");
+  registry->SetThreadName(7, "seven");
+  EXPECT_EQ(7, registry->FindThread(HasName, (void*)"seven"));
+  EXPECT_EQ(-1, registry->FindThread(HasName, (void*)"none"));
+  EXPECT_EQ(0, registry->FindThread(HasUid, (void*)get_uid(0)));
+  EXPECT_EQ(10, registry->FindThread(HasUid, (void*)get_uid(10)));
+  EXPECT_EQ(-1, registry->FindThread(HasUid, (void*)0x1234));
+  // Detach and finish and join remaining threads.
+  for (int i = 6; i <= 10; i++) {
+    registry->DetachThread(i);
+    registry->FinishThread(i);
+  }
+  for (int i = 0; i < new_tids.size(); i++) {
+    int tid = new_tids[i];
+    registry->StartThread(tid, 0, 0);
+    registry->DetachThread(tid);
+    registry->FinishThread(tid);
+  }
+  CheckThreadQuantity(registry, exp_total, 1, 1);
+  // Test methods that require the caller to hold a ThreadRegistryLock.
+  bool has_tid[16];
+  internal_memset(&has_tid[0], 0, sizeof(has_tid));
+  {
+    ThreadRegistryLock l(registry);
+    registry->RunCallbackForEachThreadLocked(MarkUidAsPresent, &has_tid[0]);
+  }
+  for (int i = 0; i < exp_total; i++) {
+    EXPECT_TRUE(has_tid[i]);
+  }
+  {
+    ThreadRegistryLock l(registry);
+    registry->CheckLocked();
+    ThreadContextBase *main_thread = registry->GetThreadLocked(0);
+    EXPECT_EQ(main_thread, registry->FindThreadContextLocked(
+        HasUid, (void*)get_uid(0)));
+  }
+  EXPECT_EQ(11, registry->GetMaxAliveThreads());
+}
+
+TEST(SanitizerCommon, ThreadRegistryTest) {
+  ThreadRegistry quarantine_registry(GetThreadContext<ThreadContextBase>,
+                                     kMaxRegistryThreads,
+                                     kRegistryQuarantine);
+  TestRegistry(&quarantine_registry, true);
+
+  ThreadRegistry no_quarantine_registry(GetThreadContext<ThreadContextBase>,
+                                        kMaxRegistryThreads,
+                                        kMaxRegistryThreads);
+  TestRegistry(&no_quarantine_registry, false);
+}
+
+static const int kThreadsPerShard = 20;
+static const int kNumShards = 25;
+
+static int num_created[kNumShards + 1];
+static int num_started[kNumShards + 1];
+static int num_joined[kNumShards + 1];
+
+namespace {
+
+struct RunThreadArgs {
+  ThreadRegistry *registry;
+  uptr shard;  // started from 1.
+};
+
+class TestThreadContext : public ThreadContextBase {
+ public:
+  explicit TestThreadContext(int tid) : ThreadContextBase(tid) {}
+  void OnJoined(void *arg) {
+    uptr shard = (uptr)arg;
+    num_joined[shard]++;
+  }
+  void OnStarted(void *arg) {
+    uptr shard = (uptr)arg;
+    num_started[shard]++;
+  }
+  void OnCreated(void *arg) {
+    uptr shard = (uptr)arg;
+    num_created[shard]++;
+  }
+};
+
+}  // namespace
+
+void *RunThread(void *arg) {
+  RunThreadArgs *args = static_cast<RunThreadArgs*>(arg);
+  std::vector<int> tids;
+  for (int i = 0; i < kThreadsPerShard; i++)
+    tids.push_back(
+        args->registry->CreateThread(0, false, 0, (void*)args->shard));
+  for (int i = 0; i < kThreadsPerShard; i++)
+    args->registry->StartThread(tids[i], 0, (void*)args->shard);
+  for (int i = 0; i < kThreadsPerShard; i++)
+    args->registry->FinishThread(tids[i]);
+  for (int i = 0; i < kThreadsPerShard; i++)
+    args->registry->JoinThread(tids[i], (void*)args->shard);
+  return 0;
+}
+
+static void ThreadedTestRegistry(ThreadRegistry *registry) {
+  // Create and start a main thread.
+  EXPECT_EQ(0, registry->CreateThread(0, true, -1, 0));
+  registry->StartThread(0, 0, 0);
+  pthread_t threads[kNumShards];
+  RunThreadArgs args[kNumShards];
+  for (int i = 0; i < kNumShards; i++) {
+    args[i].registry = registry;
+    args[i].shard = i + 1;
+    pthread_create(&threads[i], 0, RunThread, &args[i]);
+  }
+  for (int i = 0; i < kNumShards; i++) {
+    pthread_join(threads[i], 0);
+  }
+  // Check that each thread created/started/joined correct amount
+  // of "threads" in thread_registry.
+  EXPECT_EQ(1, num_created[0]);
+  EXPECT_EQ(1, num_started[0]);
+  EXPECT_EQ(0, num_joined[0]);
+  for (int i = 1; i <= kNumShards; i++) {
+    EXPECT_EQ(kThreadsPerShard, num_created[i]);
+    EXPECT_EQ(kThreadsPerShard, num_started[i]);
+    EXPECT_EQ(kThreadsPerShard, num_joined[i]);
+  }
+}
+
+TEST(SanitizerCommon, ThreadRegistryThreadedTest) {
+  ThreadRegistry registry(GetThreadContext<TestThreadContext>,
+                          kThreadsPerShard * kNumShards + 1, 10);
+  ThreadedTestRegistry(&registry);
+}
+
+}  // namespace __sanitizer





More information about the llvm-commits mailing list