1 // RUN: %clangxx_tsan -O1 %s -o %t && %env_tsan_opts=atexit_sleep_ms=0 %run %t 2>&1 | FileCheck %s
2 
3 // This test models what happens on Mac when fork
4 // calls malloc/free inside of our atfork callbacks.
5 // and ensures that we don't deadlock on malloc/free calls.
6 
7 #include "../test.h"
8 #include "syscall.h"
9 #include <errno.h>
10 #include <string.h>
11 #include <sys/types.h>
12 #include <sys/wait.h>
13 
14 // disable_sanitizer_instrumentation on __tsan_test_only_on_fork is not
15 // transitive, so we must apply it here as well.
16 // Instrumenting alloc_free_blocks() will result in deadlocks in TSan.
alloc_free_blocks()17 __attribute__((disable_sanitizer_instrumentation)) void alloc_free_blocks() {
18   // Allocate a bunch of blocks to drain local allocator cache
19   // and provoke it to lock allocator global mutexes.
20   const int kBlocks = 1000;
21   void *blocks[kBlocks];
22   for (int i = 0; i < kBlocks; i++) {
23     void *p = malloc(10);
24     *(volatile char *)p = 0;
25     blocks[i] = p;
26   }
27   for (int i = 0; i < kBlocks; i++)
28     free(blocks[i]);
29 }
30 
31 __attribute__((disable_sanitizer_instrumentation)) extern "C" void
__tsan_test_only_on_fork()32 __tsan_test_only_on_fork() {
33   const char *msg = "__tsan_test_only_on_fork\n";
34   write(2, msg, strlen(msg));
35   alloc_free_blocks();
36 }
37 
background(void * p)38 static void *background(void *p) {
39   for (;;)
40     alloc_free_blocks();
41   return 0;
42 }
43 
main()44 int main() {
45   pthread_t th;
46   pthread_create(&th, 0, background, 0);
47   pthread_detach(th);
48   for (int i = 0; i < 10; i++) {
49     int pid = myfork();
50     if (pid < 0) {
51       fprintf(stderr, "failed to fork (%d)\n", errno);
52       exit(1);
53     }
54     if (pid == 0) {
55       // child
56       exit(0);
57     }
58     // parent
59     while (wait(0) < 0) {
60     }
61   }
62   fprintf(stderr, "DONE\n");
63 }
64 
65 // CHECK: __tsan_test_only_on_fork
66 // CHECK: DONE
67