[sanitizer] Simplify sanitizer_stoptheworld_test
This commit is contained in:
		
							parent
							
								
									4f60a42878
								
							
						
					
					
						commit
						5ed03c1e10
					
				| 
						 | 
				
			
			@ -15,6 +15,7 @@
 | 
			
		|||
#include "sanitizer_common/sanitizer_platform.h"
 | 
			
		||||
#if SANITIZER_LINUX && defined(__x86_64__)
 | 
			
		||||
 | 
			
		||||
#  include <atomic>
 | 
			
		||||
#  include <mutex>
 | 
			
		||||
#  include <thread>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -24,22 +25,20 @@
 | 
			
		|||
 | 
			
		||||
namespace __sanitizer {
 | 
			
		||||
 | 
			
		||||
static std::mutex incrementer_thread_exit_mutex;
 | 
			
		||||
static std::mutex mutex;
 | 
			
		||||
 | 
			
		||||
struct CallbackArgument {
 | 
			
		||||
  volatile int counter;
 | 
			
		||||
  volatile bool threads_stopped;
 | 
			
		||||
  volatile bool callback_executed;
 | 
			
		||||
  CallbackArgument()
 | 
			
		||||
      : counter(0), threads_stopped(false), callback_executed(false) {}
 | 
			
		||||
  std::atomic_int counter = {};
 | 
			
		||||
  std::atomic_bool threads_stopped = {};
 | 
			
		||||
  std::atomic_bool callback_executed = {};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void IncrementerThread(CallbackArgument &callback_argument) {
 | 
			
		||||
  while (true) {
 | 
			
		||||
    __sync_fetch_and_add(&callback_argument.counter, 1);
 | 
			
		||||
    callback_argument.counter++;
 | 
			
		||||
 | 
			
		||||
    if (incrementer_thread_exit_mutex.try_lock()) {
 | 
			
		||||
      incrementer_thread_exit_mutex.unlock();
 | 
			
		||||
    if (mutex.try_lock()) {
 | 
			
		||||
      mutex.unlock();
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -53,11 +52,10 @@ void Callback(const SuspendedThreadsList &suspended_threads_list,
 | 
			
		|||
              void *argument) {
 | 
			
		||||
  CallbackArgument *callback_argument = (CallbackArgument *)argument;
 | 
			
		||||
  callback_argument->callback_executed = true;
 | 
			
		||||
  int counter_at_init = __sync_fetch_and_add(&callback_argument->counter, 0);
 | 
			
		||||
  int counter_at_init = callback_argument->counter;
 | 
			
		||||
  for (uptr i = 0; i < 1000; i++) {
 | 
			
		||||
    std::this_thread::yield();
 | 
			
		||||
    if (__sync_fetch_and_add(&callback_argument->counter, 0) !=
 | 
			
		||||
        counter_at_init) {
 | 
			
		||||
    if (callback_argument->counter != counter_at_init) {
 | 
			
		||||
      callback_argument->threads_stopped = false;
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -68,10 +66,11 @@ void Callback(const SuspendedThreadsList &suspended_threads_list,
 | 
			
		|||
TEST(StopTheWorld, SuspendThreadsSimple) {
 | 
			
		||||
  CallbackArgument argument;
 | 
			
		||||
  std::thread thread;
 | 
			
		||||
  incrementer_thread_exit_mutex.lock();
 | 
			
		||||
  ASSERT_NO_THROW(thread = std::thread(IncrementerThread, std::ref(argument)));
 | 
			
		||||
  {
 | 
			
		||||
    std::lock_guard<std::mutex> lock(mutex);
 | 
			
		||||
    thread = std::thread(IncrementerThread, std::ref(argument));
 | 
			
		||||
    StopTheWorld(&Callback, &argument);
 | 
			
		||||
  incrementer_thread_exit_mutex.unlock();
 | 
			
		||||
  }
 | 
			
		||||
  EXPECT_TRUE(argument.callback_executed);
 | 
			
		||||
  EXPECT_TRUE(argument.threads_stopped);
 | 
			
		||||
  // argument is on stack, so we have to wait for the incrementer thread to
 | 
			
		||||
| 
						 | 
				
			
			@ -84,43 +83,26 @@ TEST(StopTheWorld, SuspendThreadsSimple) {
 | 
			
		|||
static const uptr kThreadCount = 50;
 | 
			
		||||
static const uptr kStopWorldAfter = 10;  // let this many threads spawn first
 | 
			
		||||
 | 
			
		||||
static std::mutex advanced_incrementer_thread_exit_mutex;
 | 
			
		||||
 | 
			
		||||
struct AdvancedCallbackArgument {
 | 
			
		||||
  volatile uptr thread_index;
 | 
			
		||||
  volatile int counters[kThreadCount];
 | 
			
		||||
  std::atomic_uintptr_t thread_index = {};
 | 
			
		||||
  std::atomic_int counters[kThreadCount] = {};
 | 
			
		||||
  std::thread threads[kThreadCount];
 | 
			
		||||
  volatile bool threads_stopped;
 | 
			
		||||
  volatile bool callback_executed;
 | 
			
		||||
  volatile bool fatal_error;
 | 
			
		||||
  AdvancedCallbackArgument()
 | 
			
		||||
      : thread_index(0),
 | 
			
		||||
        threads_stopped(false),
 | 
			
		||||
        callback_executed(false),
 | 
			
		||||
        fatal_error(false) {}
 | 
			
		||||
  std::atomic_bool threads_stopped = {};
 | 
			
		||||
  std::atomic_bool callback_executed = {};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void AdvancedIncrementerThread(AdvancedCallbackArgument &callback_argument) {
 | 
			
		||||
  uptr this_thread_index =
 | 
			
		||||
      __sync_fetch_and_add(&callback_argument.thread_index, 1);
 | 
			
		||||
  uptr this_thread_index = callback_argument.thread_index++;
 | 
			
		||||
  // Spawn the next thread.
 | 
			
		||||
  if (this_thread_index + 1 < kThreadCount) {
 | 
			
		||||
    try {
 | 
			
		||||
    callback_argument.threads[this_thread_index + 1] =
 | 
			
		||||
        std::thread(AdvancedIncrementerThread, std::ref(callback_argument));
 | 
			
		||||
    } catch (...) {
 | 
			
		||||
      // Cannot use ASSERT_EQ in non-void-returning functions. If there's a
 | 
			
		||||
      // problem, defer failing to the main thread.
 | 
			
		||||
      callback_argument.fatal_error = true;
 | 
			
		||||
      __sync_fetch_and_add(&callback_argument.thread_index,
 | 
			
		||||
                           kThreadCount - callback_argument.thread_index);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  // Do the actual work.
 | 
			
		||||
  while (true) {
 | 
			
		||||
    __sync_fetch_and_add(&callback_argument.counters[this_thread_index], 1);
 | 
			
		||||
    if (advanced_incrementer_thread_exit_mutex.try_lock()) {
 | 
			
		||||
      advanced_incrementer_thread_exit_mutex.unlock();
 | 
			
		||||
    callback_argument.counters[this_thread_index]++;
 | 
			
		||||
    if (mutex.try_lock()) {
 | 
			
		||||
      mutex.unlock();
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -136,13 +118,11 @@ void AdvancedCallback(const SuspendedThreadsList &suspended_threads_list,
 | 
			
		|||
 | 
			
		||||
  int counters_at_init[kThreadCount];
 | 
			
		||||
  for (uptr j = 0; j < kThreadCount; j++)
 | 
			
		||||
    counters_at_init[j] =
 | 
			
		||||
        __sync_fetch_and_add(&callback_argument->counters[j], 0);
 | 
			
		||||
    counters_at_init[j] = callback_argument->counters[j];
 | 
			
		||||
  for (uptr i = 0; i < 10; i++) {
 | 
			
		||||
    std::this_thread::yield();
 | 
			
		||||
    for (uptr j = 0; j < kThreadCount; j++)
 | 
			
		||||
      if (__sync_fetch_and_add(&callback_argument->counters[j], 0) !=
 | 
			
		||||
          counters_at_init[j]) {
 | 
			
		||||
      if (callback_argument->counters[j] != counters_at_init[j]) {
 | 
			
		||||
        callback_argument->threads_stopped = false;
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
| 
						 | 
				
			
			@ -153,23 +133,21 @@ void AdvancedCallback(const SuspendedThreadsList &suspended_threads_list,
 | 
			
		|||
TEST(StopTheWorld, SuspendThreadsAdvanced) {
 | 
			
		||||
  AdvancedCallbackArgument argument;
 | 
			
		||||
 | 
			
		||||
  advanced_incrementer_thread_exit_mutex.lock();
 | 
			
		||||
  {
 | 
			
		||||
    std::lock_guard<std::mutex> lock(mutex);
 | 
			
		||||
    argument.threads[0] =
 | 
			
		||||
        std::thread(AdvancedIncrementerThread, std::ref(argument));
 | 
			
		||||
    // Wait for several threads to spawn before proceeding.
 | 
			
		||||
  while (__sync_fetch_and_add(&argument.thread_index, 0) < kStopWorldAfter)
 | 
			
		||||
    std::this_thread::yield();
 | 
			
		||||
    while (argument.thread_index < kStopWorldAfter) std::this_thread::yield();
 | 
			
		||||
    StopTheWorld(&AdvancedCallback, &argument);
 | 
			
		||||
    EXPECT_TRUE(argument.callback_executed);
 | 
			
		||||
    EXPECT_TRUE(argument.threads_stopped);
 | 
			
		||||
 | 
			
		||||
    // Wait for all threads to spawn before we start terminating them.
 | 
			
		||||
  while (__sync_fetch_and_add(&argument.thread_index, 0) < kThreadCount)
 | 
			
		||||
    std::this_thread::yield();
 | 
			
		||||
  ASSERT_FALSE(argument.fatal_error);  // a thread could not be started
 | 
			
		||||
    while (argument.thread_index < kThreadCount) std::this_thread::yield();
 | 
			
		||||
  }
 | 
			
		||||
  // Signal the threads to terminate.
 | 
			
		||||
  advanced_incrementer_thread_exit_mutex.unlock();
 | 
			
		||||
  for (uptr i = 0; i < kThreadCount; i++) argument.threads[i].join();
 | 
			
		||||
  for (auto &t : argument.threads) t.join();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void SegvCallback(const SuspendedThreadsList &suspended_threads_list,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue