Home | History | Annotate | Download | only in tests
      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