Home | History | Annotate | Download | only in tsan
      1 // RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
      2 
      3 // Test case for recursive signal handlers, adopted from:
      4 // https://code.google.com/p/thread-sanitizer/issues/detail?id=71
      5 
      6 #include "test.h"
      7 #include <semaphore.h>
      8 #include <signal.h>
      9 #include <errno.h>
     10 
     11 static const int kSigSuspend = SIGUSR1;
     12 static const int kSigRestart = SIGUSR2;
     13 
     14 static sem_t g_thread_suspend_ack_sem;
     15 
     16 static bool g_busy_thread_received_restart;
     17 
     18 static volatile bool g_busy_thread_garbage_collected;
     19 
     20 static void SaveRegistersInStack() {
     21   // Mono walks thread stacks to detect unreferenced objects.
     22   // If last object reference is kept in register the object will be collected
     23   // This is why threads can't be suspended with something like pthread_suspend
     24 }
     25 
     26 static void fail(const char *what) {
     27   fprintf(stderr, "FAILED: %s (errno=%d)\n", what, errno);
     28   exit(1);
     29 }
     30 
     31 static void SuspendHandler(int sig) {
     32   int old_errno = errno;
     33   SaveRegistersInStack();
     34 
     35   // Enable kSigRestart handling, tsan disables signals around signal handlers.
     36   sigset_t sigset;
     37   sigemptyset(&sigset);
     38   pthread_sigmask(SIG_SETMASK, &sigset, 0);
     39 
     40   // Acknowledge that thread is saved and suspended
     41   if (sem_post(&g_thread_suspend_ack_sem) != 0)
     42     fail("sem_post failed");
     43 
     44   // Wait for wakeup signal.
     45   while (!g_busy_thread_received_restart)
     46     usleep(100);  // wait for kSigRestart signal
     47 
     48   // Acknowledge that thread restarted
     49   if (sem_post(&g_thread_suspend_ack_sem) != 0)
     50     fail("sem_post failed");
     51 
     52   g_busy_thread_garbage_collected = true;
     53 
     54   errno = old_errno;
     55 }
     56 
     57 static void RestartHandler(int sig) {
     58   g_busy_thread_received_restart = true;
     59 }
     60 
     61 static void StopWorld(pthread_t thread) {
     62   if (pthread_kill(thread, kSigSuspend) != 0)
     63     fail("pthread_kill failed");
     64 
     65   while (sem_wait(&g_thread_suspend_ack_sem) != 0) {
     66     if (errno != EINTR)
     67       fail("sem_wait failed");
     68   }
     69 }
     70 
     71 static void StartWorld(pthread_t thread) {
     72   if (pthread_kill(thread, kSigRestart) != 0)
     73     fail("pthread_kill failed");
     74 
     75   while (sem_wait(&g_thread_suspend_ack_sem) != 0) {
     76     if (errno != EINTR)
     77       fail("sem_wait failed");
     78   }
     79 }
     80 
     81 static void CollectGarbage(pthread_t thread) {
     82   StopWorld(thread);
     83   // Walk stacks
     84   StartWorld(thread);
     85 }
     86 
     87 static void Init() {
     88   if (sem_init(&g_thread_suspend_ack_sem, 0, 0) != 0)
     89     fail("sem_init failed");
     90 
     91   struct sigaction act = {};
     92   act.sa_flags = SA_RESTART;
     93   act.sa_handler = &SuspendHandler;
     94   if (sigaction(kSigSuspend, &act, NULL) != 0)
     95     fail("sigaction failed");
     96   act.sa_handler = &RestartHandler;
     97   if (sigaction(kSigRestart, &act, NULL) != 0)
     98     fail("sigaction failed");
     99 }
    100 
    101 void* BusyThread(void *arg) {
    102   (void)arg;
    103   while (!g_busy_thread_garbage_collected) {
    104     usleep(100); // Tsan deadlocks without these sleeps
    105   }
    106   return NULL;
    107 }
    108 
    109 int main(int argc, const char *argv[]) {
    110   Init();
    111   pthread_t busy_thread;
    112   if (pthread_create(&busy_thread, NULL, &BusyThread, NULL) != 0)
    113     fail("pthread_create failed");
    114   CollectGarbage(busy_thread);
    115   if (pthread_join(busy_thread, 0) != 0)
    116     fail("pthread_join failed");
    117   fprintf(stderr, "DONE\n");
    118   return 0;
    119 }
    120 
    121 // CHECK-NOT: FAILED
    122 // CHECK-NOT: ThreadSanitizer CHECK failed
    123 // CHECK-NOT: WARNING: ThreadSanitizer:
    124 // CHECK: DONE
    125