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