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.
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(size: 10);
24 *(volatile char *)p = 0;
25 blocks[i] = p;
26 }
27 for (int i = 0; i < kBlocks; i++)
28 free(ptr: blocks[i]);
29}
30
31__attribute__((disable_sanitizer_instrumentation)) extern "C" void
32__tsan_test_only_on_fork() {
33 const char *msg = "__tsan_test_only_on_fork\n";
34 write(fd: 2, buf: msg, n: strlen(s: msg));
35 alloc_free_blocks();
36}
37
38static void *background(void *p) {
39 for (;;)
40 alloc_free_blocks();
41 return 0;
42}
43
44int main() {
45 pthread_t th;
46 pthread_create(newthread: &th, attr: 0, start_routine: background, arg: 0);
47 pthread_detach(th: th);
48 for (int i = 0; i < 10; i++) {
49 int pid = myfork();
50 if (pid < 0) {
51 fprintf(stderr, format: "failed to fork (%d)\n", errno);
52 exit(status: 1);
53 }
54 if (pid == 0) {
55 // child
56 exit(status: 0);
57 }
58 // parent
59 while (wait(stat_loc: 0) < 0) {
60 }
61 }
62 fprintf(stderr, format: "DONE\n");
63}
64
65// CHECK: __tsan_test_only_on_fork
66// CHECK: DONE
67

source code of compiler-rt/test/tsan/Linux/fork_deadlock.cpp