| 1 | // This run stresses global reset happenning concurrently with everything else. |
| 2 | // RUN: %clangxx_tsan -O1 %s -o %t && %env_tsan_opts=flush_memory_ms=1:flush_symbolizer_ms=1:memory_limit_mb=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-NORACE |
| 3 | // This run stresses race reporting happenning concurrently with everything else. |
| 4 | // RUN: %clangxx_tsan -O1 %s -DRACE=1 -o %t && %env_tsan_opts=suppress_equal_stacks=0 %deflake %run %t | FileCheck %s --check-prefix=CHECK-RACE |
| 5 | #include "test.h" |
| 6 | #include <fcntl.h> |
| 7 | #include <string.h> |
| 8 | |
| 9 | volatile long stop; |
| 10 | long atomic, read_only, racy; |
| 11 | int fds[2]; |
| 12 | |
| 13 | __attribute__((noinline)) void *SecondaryThread(void *x) { |
| 14 | __atomic_fetch_add(&atomic, 1, __ATOMIC_ACQ_REL); |
| 15 | return NULL; |
| 16 | } |
| 17 | |
| 18 | void *Thread(void *x) { |
| 19 | const int me = (long)x; |
| 20 | volatile long sink = 0; |
| 21 | int fd = -1; |
| 22 | while (!stop) { |
| 23 | // If me == 0, we do all of the following, |
| 24 | // otherwise only 1 type of action. |
| 25 | if (me == 0 || me == 1) { |
| 26 | // just read the stop variable |
| 27 | } |
| 28 | if (me == 0 || me == 2) { |
| 29 | __atomic_store_n(&atomic, sink, __ATOMIC_RELEASE); |
| 30 | } |
| 31 | if (me == 0 || me == 3) { |
| 32 | sink += __atomic_fetch_add(&atomic, 1, __ATOMIC_ACQ_REL); |
| 33 | } |
| 34 | if (me == 0 || me == 4) { |
| 35 | SecondaryThread(NULL); |
| 36 | } |
| 37 | if (me == 0 || me == 5) { |
| 38 | write(fd: fds[1], buf: fds, n: 1); |
| 39 | } |
| 40 | if (me == 0 || me == 6) { |
| 41 | char buf[2]; |
| 42 | read(fd: fds[0], buf: &buf, nbytes: sizeof(buf)); |
| 43 | } |
| 44 | if (me == 0 || me == 7) { |
| 45 | pthread_t th; |
| 46 | pthread_create(newthread: &th, NULL, start_routine: SecondaryThread, NULL); |
| 47 | pthread_join(th: th, NULL); |
| 48 | } |
| 49 | if (me == 0 || me == 8) { |
| 50 | long buf; |
| 51 | memcpy(dest: &buf, src: &read_only, n: sizeof(buf)); |
| 52 | sink += buf; |
| 53 | } |
| 54 | if (me == 0 || me == 9) { |
| 55 | #if RACE |
| 56 | sink += racy++; |
| 57 | #else |
| 58 | sink += racy; |
| 59 | #endif |
| 60 | } |
| 61 | if (me == 0 || me == 10) { |
| 62 | fd = open(file: "/dev/null" , O_RDONLY); |
| 63 | if (fd != -1) { |
| 64 | close(fd: fd); |
| 65 | fd = -1; |
| 66 | } |
| 67 | } |
| 68 | // If you add more actions, update kActions in main. |
| 69 | } |
| 70 | return NULL; |
| 71 | } |
| 72 | |
| 73 | int main() { |
| 74 | ANNOTATE_BENIGN_RACE(stop); |
| 75 | if (pipe(pipedes: fds)) |
| 76 | exit(status: (perror(s: "pipe" ), 1)); |
| 77 | if (fcntl(fd: fds[0], F_SETFL, O_NONBLOCK)) |
| 78 | exit(status: (perror(s: "fcntl" ), 1)); |
| 79 | if (fcntl(fd: fds[1], F_SETFL, O_NONBLOCK)) |
| 80 | exit(status: (perror(s: "fcntl" ), 1)); |
| 81 | const int kActions = 11; |
| 82 | #if RACE |
| 83 | const int kMultiplier = 1; |
| 84 | #else |
| 85 | const int kMultiplier = 4; |
| 86 | #endif |
| 87 | pthread_t t[kActions * kMultiplier]; |
| 88 | for (int i = 0; i < kActions * kMultiplier; i++) |
| 89 | pthread_create(newthread: &t[i], NULL, start_routine: Thread, arg: (void *)(long)(i % kActions)); |
| 90 | sleep(seconds: 5); |
| 91 | stop = 1; |
| 92 | for (int i = 0; i < kActions * kMultiplier; i++) |
| 93 | pthread_join(th: t[i], NULL); |
| 94 | fprintf(stderr, format: "DONE\n" ); |
| 95 | return 0; |
| 96 | } |
| 97 | |
| 98 | // CHECK-NORACE-NOT: ThreadSanitizer: |
| 99 | // CHECK-NORACE: DONE |
| 100 | // CHECK-NORACE-NOT: ThreadSanitizer: |
| 101 | // CHECK-RACE: ThreadSanitizer: data race |
| 102 | // CHECK-RACE: DONE |
| 103 | |