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
26namespace __sanitizer {
27
28static pthread_mutex_t incrementer_thread_exit_mutex;
29
30struct CallbackArgument {
31  volatile int counter;
32  volatile bool threads_stopped;
33  volatile bool callback_executed;
34  CallbackArgument()
35    : counter(0),
36      threads_stopped(false),
37      callback_executed(false) {}
38};
39
40void *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.
55void 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
71TEST(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.
92static const uptr kThreadCount = 50;
93static const uptr kStopWorldAfter = 10; // let this many threads spawn first
94
95static pthread_mutex_t advanced_incrementer_thread_exit_mutex;
96
97struct 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;
104  AdvancedCallbackArgument()
105    : thread_index(0),
106      threads_stopped(false),
107      callback_executed(false),
108      fatal_error(false) {}
109};
110
111void *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
142void 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
164TEST(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