[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)));
|
||||
StopTheWorld(&Callback, &argument);
|
||||
incrementer_thread_exit_mutex.unlock();
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
thread = std::thread(IncrementerThread, std::ref(argument));
|
||||
StopTheWorld(&Callback, &argument);
|
||||
}
|
||||
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);
|
||||
}
|
||||
callback_argument.threads[this_thread_index + 1] =
|
||||
std::thread(AdvancedIncrementerThread, std::ref(callback_argument));
|
||||
}
|
||||
// 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();
|
||||
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();
|
||||
StopTheWorld(&AdvancedCallback, &argument);
|
||||
EXPECT_TRUE(argument.callback_executed);
|
||||
EXPECT_TRUE(argument.threads_stopped);
|
||||
{
|
||||
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 (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
|
||||
// Wait for all threads to spawn before we start terminating them.
|
||||
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