sanitizer_stoptheworld_test.cc revision 5efb53ed40eeab7bc6c361c0825608fc97322e1d
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
25namespace __sanitizer {
26
27static pthread_mutex_t incrementer_thread_exit_mutex;
28
29struct 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
39void *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.
54void 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
70TEST(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.
91static const uptr kThreadCount = 50;
92static const uptr kStopWorldAfter = 10; // let this many threads spawn first
93
94static pthread_mutex_t advanced_incrementer_thread_exit_mutex;
95
96struct 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
110void *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
141void 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
163TEST(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