1 //===-- sanitizer_stoptheworld_test.cpp -----------------------------------===//
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
7 //===----------------------------------------------------------------------===//
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__)
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
) {
38 callback_argument
.counter
++;
40 if (mutex
.try_lock()) {
45 std::this_thread::yield();
49 // This callback checks that IncrementerThread is suspended at the time of its
51 void Callback(const SuspendedThreadsList
&suspended_threads_list
,
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;
63 callback_argument
->threads_stopped
= true;
66 TEST(StopTheWorld
, SuspendThreadsSimple
) {
67 CallbackArgument argument
;
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.
103 callback_argument
.counters
[this_thread_index
]++;
104 if (mutex
.try_lock()) {
109 std::this_thread::yield();
113 void AdvancedCallback(const SuspendedThreadsList
&suspended_threads_list
,
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;
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
,
155 *(volatile int *)0x1234 = 0;
158 # if SANITIZER_WINDOWS
159 # define MAYBE_SegvInCallback DISABLED_SegvInCallback
161 # define MAYBE_SegvInCallback SegvInCallback
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__)