1 | // RUN: %clang_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s |
2 | // Regression test for |
3 | // https://groups.google.com/g/thread-sanitizer/c/TQrr4-9PRYo/m/HFR4FMi6AQAJ |
4 | #include "test.h" |
5 | #include <sys/types.h> |
6 | #include <sys/wait.h> |
7 | #include <errno.h> |
8 | #include <string.h> |
9 | #include <signal.h> |
10 | |
11 | long glob = 0; |
12 | |
13 | void *worker(void *main) { |
14 | glob++; |
15 | // synchronize with main |
16 | barrier_wait(&barrier); |
17 | // synchronize with atfork |
18 | barrier_wait(&barrier); |
19 | pthread_kill((pthread_t)main, SIGPROF); |
20 | barrier_wait(&barrier); |
21 | // synchronize with afterfork |
22 | barrier_wait(&barrier); |
23 | pthread_kill((pthread_t)main, SIGPROF); |
24 | barrier_wait(&barrier); |
25 | return NULL; |
26 | } |
27 | |
28 | void atfork() { |
29 | barrier_wait(&barrier); |
30 | barrier_wait(&barrier); |
31 | write(2, "in atfork\n" , strlen("in atfork\n" )); |
32 | static volatile long a; |
33 | __atomic_fetch_add(&a, 1, __ATOMIC_RELEASE); |
34 | } |
35 | |
36 | void afterfork() { |
37 | barrier_wait(&barrier); |
38 | barrier_wait(&barrier); |
39 | write(2, "in afterfork\n" , strlen("in afterfork\n" )); |
40 | static volatile long a; |
41 | __atomic_fetch_add(&a, 1, __ATOMIC_RELEASE); |
42 | } |
43 | |
44 | void afterfork_child() { |
45 | // Can't synchronize with barriers because we are |
46 | // in the new process, but want consistent output. |
47 | sleep(1); |
48 | write(2, "in afterfork_child\n" , strlen("in afterfork_child\n" )); |
49 | glob++; |
50 | } |
51 | |
52 | void handler(int sig) { |
53 | write(2, "in handler\n" , strlen("in handler\n" )); |
54 | glob++; |
55 | } |
56 | |
57 | int main() { |
58 | barrier_init(&barrier, 2); |
59 | struct sigaction act = {}; |
60 | act.sa_handler = &handler; |
61 | if (sigaction(SIGPROF, &act, 0)) { |
62 | perror("sigaction" ); |
63 | exit(1); |
64 | } |
65 | pthread_atfork(atfork, afterfork, afterfork_child); |
66 | pthread_t t; |
67 | pthread_create(&t, NULL, worker, (void*)pthread_self()); |
68 | barrier_wait(&barrier); |
69 | pid_t pid = fork(); |
70 | if (pid < 0) { |
71 | fprintf(stderr, "fork failed: %d\n" , errno); |
72 | return 1; |
73 | } |
74 | if (pid == 0) { |
75 | fprintf(stderr, "CHILD\n" ); |
76 | return 0; |
77 | } |
78 | if (pid != waitpid(pid, NULL, 0)) { |
79 | fprintf(stderr, "waitpid failed: %d\n" , errno); |
80 | return 1; |
81 | } |
82 | pthread_join(t, NULL); |
83 | fprintf(stderr, "PARENT\n" ); |
84 | return 0; |
85 | } |
86 | |
87 | // CHECK: in atfork |
88 | // CHECK: in handler |
89 | // CHECK: ThreadSanitizer: data race |
90 | // CHECK: Write of size 8 |
91 | // CHECK: #0 handler |
92 | // CHECK: Previous write of size 8 |
93 | // CHECK: #0 worker |
94 | // CHECK: afterfork |
95 | // CHECK: in handler |
96 | // CHECK: afterfork_child |
97 | // CHECK: CHILD |
98 | // CHECK: PARENT |
99 | |