• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===-- sanitizer_stoptheworld_test.cc ------------------------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // Tests for sanitizer_stoptheworld.h
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "sanitizer_common/sanitizer_platform.h"
15 #if SANITIZER_LINUX && defined(__x86_64__)
16 
17 #include "sanitizer_common/sanitizer_stoptheworld.h"
18 #include "gtest/gtest.h"
19 
20 #include "sanitizer_common/sanitizer_libc.h"
21 #include "sanitizer_common/sanitizer_common.h"
22 
23 #include <pthread.h>
24 #include <sched.h>
25 
26 namespace __sanitizer {
27 
28 static pthread_mutex_t incrementer_thread_exit_mutex;
29 
30 struct CallbackArgument {
31   volatile int counter;
32   volatile bool threads_stopped;
33   volatile bool callback_executed;
CallbackArgument__sanitizer::CallbackArgument34   CallbackArgument()
35     : counter(0),
36       threads_stopped(false),
37       callback_executed(false) {}
38 };
39 
IncrementerThread(void * argument)40 void *IncrementerThread(void *argument) {
41   CallbackArgument *callback_argument = (CallbackArgument *)argument;
42   while (true) {
43     __sync_fetch_and_add(&callback_argument->counter, 1);
44     if (pthread_mutex_trylock(&incrementer_thread_exit_mutex) == 0) {
45       pthread_mutex_unlock(&incrementer_thread_exit_mutex);
46       return NULL;
47     } else {
48       sched_yield();
49     }
50   }
51 }
52 
53 // This callback checks that IncrementerThread is suspended at the time of its
54 // execution.
Callback(const SuspendedThreadsList & suspended_threads_list,void * argument)55 void Callback(const SuspendedThreadsList &suspended_threads_list,
56               void *argument) {
57   CallbackArgument *callback_argument = (CallbackArgument *)argument;
58   callback_argument->callback_executed = true;
59   int counter_at_init = __sync_fetch_and_add(&callback_argument->counter, 0);
60   for (uptr i = 0; i < 1000; i++) {
61     sched_yield();
62     if (__sync_fetch_and_add(&callback_argument->counter, 0) !=
63           counter_at_init) {
64       callback_argument->threads_stopped = false;
65       return;
66     }
67   }
68   callback_argument->threads_stopped = true;
69 }
70 
TEST(StopTheWorld,SuspendThreadsSimple)71 TEST(StopTheWorld, SuspendThreadsSimple) {
72   pthread_mutex_init(&incrementer_thread_exit_mutex, NULL);
73   CallbackArgument argument;
74   pthread_t thread_id;
75   int pthread_create_result;
76   pthread_mutex_lock(&incrementer_thread_exit_mutex);
77   pthread_create_result = pthread_create(&thread_id, NULL, IncrementerThread,
78                                          &argument);
79   ASSERT_EQ(0, pthread_create_result);
80   StopTheWorld(&Callback, &argument);
81   pthread_mutex_unlock(&incrementer_thread_exit_mutex);
82   EXPECT_TRUE(argument.callback_executed);
83   EXPECT_TRUE(argument.threads_stopped);
84   // argument is on stack, so we have to wait for the incrementer thread to
85   // terminate before we can return from this function.
86   ASSERT_EQ(0, pthread_join(thread_id, NULL));
87   pthread_mutex_destroy(&incrementer_thread_exit_mutex);
88 }
89 
90 // A more comprehensive test where we spawn a bunch of threads while executing
91 // StopTheWorld in parallel.
92 static const uptr kThreadCount = 50;
93 static const uptr kStopWorldAfter = 10; // let this many threads spawn first
94 
95 static pthread_mutex_t advanced_incrementer_thread_exit_mutex;
96 
97 struct AdvancedCallbackArgument {
98   volatile uptr thread_index;
99   volatile int counters[kThreadCount];
100   pthread_t thread_ids[kThreadCount];
101   volatile bool threads_stopped;
102   volatile bool callback_executed;
103   volatile bool fatal_error;
AdvancedCallbackArgument__sanitizer::AdvancedCallbackArgument104   AdvancedCallbackArgument()
105     : thread_index(0),
106       threads_stopped(false),
107       callback_executed(false),
108       fatal_error(false) {}
109 };
110 
AdvancedIncrementerThread(void * argument)111 void *AdvancedIncrementerThread(void *argument) {
112   AdvancedCallbackArgument *callback_argument =
113       (AdvancedCallbackArgument *)argument;
114   uptr this_thread_index = __sync_fetch_and_add(
115       &callback_argument->thread_index, 1);
116   // Spawn the next thread.
117   int pthread_create_result;
118   if (this_thread_index + 1 < kThreadCount) {
119     pthread_create_result =
120         pthread_create(&callback_argument->thread_ids[this_thread_index + 1],
121                        NULL, AdvancedIncrementerThread, argument);
122     // Cannot use ASSERT_EQ in non-void-returning functions. If there's a
123     // problem, defer failing to the main thread.
124     if (pthread_create_result != 0) {
125       callback_argument->fatal_error = true;
126       __sync_fetch_and_add(&callback_argument->thread_index,
127                            kThreadCount - callback_argument->thread_index);
128     }
129   }
130   // Do the actual work.
131   while (true) {
132     __sync_fetch_and_add(&callback_argument->counters[this_thread_index], 1);
133     if (pthread_mutex_trylock(&advanced_incrementer_thread_exit_mutex) == 0) {
134       pthread_mutex_unlock(&advanced_incrementer_thread_exit_mutex);
135       return NULL;
136     } else {
137       sched_yield();
138     }
139   }
140 }
141 
AdvancedCallback(const SuspendedThreadsList & suspended_threads_list,void * argument)142 void AdvancedCallback(const SuspendedThreadsList &suspended_threads_list,
143                              void *argument) {
144   AdvancedCallbackArgument *callback_argument =
145       (AdvancedCallbackArgument *)argument;
146   callback_argument->callback_executed = true;
147 
148   int counters_at_init[kThreadCount];
149   for (uptr j = 0; j < kThreadCount; j++)
150     counters_at_init[j] = __sync_fetch_and_add(&callback_argument->counters[j],
151                                                0);
152   for (uptr i = 0; i < 10; i++) {
153     sched_yield();
154     for (uptr j = 0; j < kThreadCount; j++)
155       if (__sync_fetch_and_add(&callback_argument->counters[j], 0) !=
156             counters_at_init[j]) {
157         callback_argument->threads_stopped = false;
158         return;
159       }
160   }
161   callback_argument->threads_stopped = true;
162 }
163 
TEST(StopTheWorld,SuspendThreadsAdvanced)164 TEST(StopTheWorld, SuspendThreadsAdvanced) {
165   pthread_mutex_init(&advanced_incrementer_thread_exit_mutex, NULL);
166   AdvancedCallbackArgument argument;
167 
168   pthread_mutex_lock(&advanced_incrementer_thread_exit_mutex);
169   int pthread_create_result;
170   pthread_create_result = pthread_create(&argument.thread_ids[0], NULL,
171                                          AdvancedIncrementerThread,
172                                          &argument);
173   ASSERT_EQ(0, pthread_create_result);
174   // Wait for several threads to spawn before proceeding.
175   while (__sync_fetch_and_add(&argument.thread_index, 0) < kStopWorldAfter)
176     sched_yield();
177   StopTheWorld(&AdvancedCallback, &argument);
178   EXPECT_TRUE(argument.callback_executed);
179   EXPECT_TRUE(argument.threads_stopped);
180 
181   // Wait for all threads to spawn before we start terminating them.
182   while (__sync_fetch_and_add(&argument.thread_index, 0) < kThreadCount)
183     sched_yield();
184   ASSERT_FALSE(argument.fatal_error); // a pthread_create has failed
185   // Signal the threads to terminate.
186   pthread_mutex_unlock(&advanced_incrementer_thread_exit_mutex);
187   for (uptr i = 0; i < kThreadCount; i++)
188     ASSERT_EQ(0, pthread_join(argument.thread_ids[i], NULL));
189   pthread_mutex_destroy(&advanced_incrementer_thread_exit_mutex);
190 }
191 
192 }  // namespace __sanitizer
193 
194 #endif  // SANITIZER_LINUX && defined(__x86_64__)
195