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
11static const int kSigSuspend = SIGUSR1;
12static const int kSigRestart = SIGUSR2;
13
14static sem_t g_thread_suspend_ack_sem;
15
16static bool g_busy_thread_received_restart;
17
18static volatile bool g_busy_thread_garbage_collected;
19
20static 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
26static void fail(const char *what) {
27  fprintf(stderr, "FAILED: %s (errno=%d)\n", what, errno);
28  exit(1);
29}
30
31static 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
57static void RestartHandler(int sig) {
58  g_busy_thread_received_restart = true;
59}
60
61static 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
71static 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
81static void CollectGarbage(pthread_t thread) {
82  StopWorld(thread);
83  // Walk stacks
84  StartWorld(thread);
85}
86
87static 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
101void* 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
109int 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