Run DCE after a LoopFlatten test to reduce spurious output [nfc]
[llvm-project.git] / compiler-rt / lib / sanitizer_common / tests / sanitizer_stoptheworld_test.cpp
blobc2a269084d0f27d6975a080eb2cf40f9144a62b6
1 //===-- sanitizer_stoptheworld_test.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-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Tests for sanitizer_stoptheworld.h
11 //===----------------------------------------------------------------------===//
13 #include "sanitizer_common/sanitizer_stoptheworld.h"
15 #include "sanitizer_common/sanitizer_platform.h"
16 #if (SANITIZER_LINUX || SANITIZER_WINDOWS) && defined(__x86_64__)
18 # include <atomic>
19 # include <mutex>
20 # include <thread>
22 # include "gtest/gtest.h"
23 # include "sanitizer_common/sanitizer_common.h"
24 # include "sanitizer_common/sanitizer_libc.h"
26 namespace __sanitizer {
28 static std::mutex mutex;
30 struct CallbackArgument {
31 std::atomic_int counter = {};
32 std::atomic_bool threads_stopped = {};
33 std::atomic_bool callback_executed = {};
36 void IncrementerThread(CallbackArgument &callback_argument) {
37 while (true) {
38 callback_argument.counter++;
40 if (mutex.try_lock()) {
41 mutex.unlock();
42 return;
45 std::this_thread::yield();
49 // This callback checks that IncrementerThread is suspended at the time of its
50 // execution.
51 void Callback(const SuspendedThreadsList &suspended_threads_list,
52 void *argument) {
53 CallbackArgument *callback_argument = (CallbackArgument *)argument;
54 callback_argument->callback_executed = true;
55 int counter_at_init = callback_argument->counter;
56 for (uptr i = 0; i < 1000; i++) {
57 std::this_thread::yield();
58 if (callback_argument->counter != counter_at_init) {
59 callback_argument->threads_stopped = false;
60 return;
63 callback_argument->threads_stopped = true;
66 TEST(StopTheWorld, SuspendThreadsSimple) {
67 CallbackArgument argument;
68 std::thread thread;
70 std::lock_guard<std::mutex> lock(mutex);
71 thread = std::thread(IncrementerThread, std::ref(argument));
72 StopTheWorld(&Callback, &argument);
74 EXPECT_TRUE(argument.callback_executed);
75 EXPECT_TRUE(argument.threads_stopped);
76 // argument is on stack, so we have to wait for the incrementer thread to
77 // terminate before we can return from this function.
78 ASSERT_NO_THROW(thread.join());
81 // A more comprehensive test where we spawn a bunch of threads while executing
82 // StopTheWorld in parallel.
83 static const uptr kThreadCount = 50;
84 static const uptr kStopWorldAfter = 10; // let this many threads spawn first
86 struct AdvancedCallbackArgument {
87 std::atomic_uintptr_t thread_index = {};
88 std::atomic_int counters[kThreadCount] = {};
89 std::thread threads[kThreadCount];
90 std::atomic_bool threads_stopped = {};
91 std::atomic_bool callback_executed = {};
94 void AdvancedIncrementerThread(AdvancedCallbackArgument &callback_argument) {
95 uptr this_thread_index = callback_argument.thread_index++;
96 // Spawn the next thread.
97 if (this_thread_index + 1 < kThreadCount) {
98 callback_argument.threads[this_thread_index + 1] =
99 std::thread(AdvancedIncrementerThread, std::ref(callback_argument));
101 // Do the actual work.
102 while (true) {
103 callback_argument.counters[this_thread_index]++;
104 if (mutex.try_lock()) {
105 mutex.unlock();
106 return;
109 std::this_thread::yield();
113 void AdvancedCallback(const SuspendedThreadsList &suspended_threads_list,
114 void *argument) {
115 AdvancedCallbackArgument *callback_argument =
116 (AdvancedCallbackArgument *)argument;
117 callback_argument->callback_executed = true;
119 int counters_at_init[kThreadCount];
120 for (uptr j = 0; j < kThreadCount; j++)
121 counters_at_init[j] = callback_argument->counters[j];
122 for (uptr i = 0; i < 10; i++) {
123 std::this_thread::yield();
124 for (uptr j = 0; j < kThreadCount; j++)
125 if (callback_argument->counters[j] != counters_at_init[j]) {
126 callback_argument->threads_stopped = false;
127 return;
130 callback_argument->threads_stopped = true;
133 TEST(StopTheWorld, SuspendThreadsAdvanced) {
134 AdvancedCallbackArgument argument;
137 std::lock_guard<std::mutex> lock(mutex);
138 argument.threads[0] =
139 std::thread(AdvancedIncrementerThread, std::ref(argument));
140 // Wait for several threads to spawn before proceeding.
141 while (argument.thread_index < kStopWorldAfter) std::this_thread::yield();
142 StopTheWorld(&AdvancedCallback, &argument);
143 EXPECT_TRUE(argument.callback_executed);
144 EXPECT_TRUE(argument.threads_stopped);
146 // Wait for all threads to spawn before we start terminating them.
147 while (argument.thread_index < kThreadCount) std::this_thread::yield();
149 // Signal the threads to terminate.
150 for (auto &t : argument.threads) t.join();
153 static void SegvCallback(const SuspendedThreadsList &suspended_threads_list,
154 void *argument) {
155 *(volatile int *)0x1234 = 0;
158 # if SANITIZER_WINDOWS
159 # define MAYBE_SegvInCallback DISABLED_SegvInCallback
160 # else
161 # define MAYBE_SegvInCallback SegvInCallback
162 # endif
164 TEST(StopTheWorld, MAYBE_SegvInCallback) {
165 // Test that tracer thread catches SIGSEGV.
166 StopTheWorld(&SegvCallback, NULL);
169 } // namespace __sanitizer
171 #endif // SANITIZER_LINUX && defined(__x86_64__)