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