xref: /aosp_15_r20/external/compiler-rt/test/tsan/signal_recursive.cc (revision 7c3d14c8b49c529e04be81a3ce6f5cc23712e4c6)
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