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