| // RUN: %clangxx_tsan -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s |
| #include <pthread.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <stdint.h> |
| #include <unistd.h> |
| |
| #define NOINLINE __attribute__((noinline)) |
| |
| volatile uint64_t objs[8*2*(2 + 4 + 8)][2]; |
| |
| extern "C" { |
| uint16_t __sanitizer_unaligned_load16(volatile void *addr); |
| uint32_t __sanitizer_unaligned_load32(volatile void *addr); |
| uint64_t __sanitizer_unaligned_load64(volatile void *addr); |
| void __sanitizer_unaligned_store16(volatile void *addr, uint16_t v); |
| void __sanitizer_unaligned_store32(volatile void *addr, uint32_t v); |
| void __sanitizer_unaligned_store64(volatile void *addr, uint64_t v); |
| } |
| |
| // All this mess is to generate unique stack for each race, |
| // otherwise tsan will suppress similar stacks. |
| |
| static NOINLINE void access(volatile char *p, int sz, int rw) { |
| if (rw) { |
| switch (sz) { |
| case 0: __sanitizer_unaligned_store16(p, 0); break; |
| case 1: __sanitizer_unaligned_store32(p, 0); break; |
| case 2: __sanitizer_unaligned_store64(p, 0); break; |
| default: exit(1); |
| } |
| } else { |
| switch (sz) { |
| case 0: __sanitizer_unaligned_load16(p); break; |
| case 1: __sanitizer_unaligned_load32(p); break; |
| case 2: __sanitizer_unaligned_load64(p); break; |
| default: exit(1); |
| } |
| } |
| } |
| |
| static int accesssize(int sz) { |
| switch (sz) { |
| case 0: return 2; |
| case 1: return 4; |
| case 2: return 8; |
| } |
| exit(1); |
| } |
| |
| template<int off, int off2> |
| static NOINLINE void access3(bool main, int sz1, bool rw, volatile char *p) { |
| p += off; |
| if (main) { |
| access(p, sz1, true); |
| } else { |
| p += off2; |
| if (rw) { |
| *p = 42; |
| } else { |
| if (*p == 42) |
| printf("bingo!\n"); |
| } |
| } |
| } |
| |
| template<int off> |
| static NOINLINE void |
| access2(bool main, int sz1, int off2, bool rw, volatile char *obj) { |
| if (off2 == 0) |
| access3<off, 0>(main, sz1, rw, obj); |
| else if (off2 == 1) |
| access3<off, 1>(main, sz1, rw, obj); |
| else if (off2 == 2) |
| access3<off, 2>(main, sz1, rw, obj); |
| else if (off2 == 3) |
| access3<off, 3>(main, sz1, rw, obj); |
| else if (off2 == 4) |
| access3<off, 4>(main, sz1, rw, obj); |
| else if (off2 == 5) |
| access3<off, 5>(main, sz1, rw, obj); |
| else if (off2 == 6) |
| access3<off, 6>(main, sz1, rw, obj); |
| else if (off2 == 7) |
| access3<off, 7>(main, sz1, rw, obj); |
| } |
| |
| static NOINLINE void |
| access1(bool main, int off, int sz1, int off2, bool rw, char *obj) { |
| if (off == 0) |
| access2<0>(main, sz1, off2, rw, obj); |
| else if (off == 1) |
| access2<1>(main, sz1, off2, rw, obj); |
| else if (off == 2) |
| access2<2>(main, sz1, off2, rw, obj); |
| else if (off == 3) |
| access2<3>(main, sz1, off2, rw, obj); |
| else if (off == 4) |
| access2<4>(main, sz1, off2, rw, obj); |
| else if (off == 5) |
| access2<5>(main, sz1, off2, rw, obj); |
| else if (off == 6) |
| access2<6>(main, sz1, off2, rw, obj); |
| else if (off == 7) |
| access2<7>(main, sz1, off2, rw, obj); |
| } |
| |
| NOINLINE void Test(bool main) { |
| volatile uint64_t *obj = objs[0]; |
| for (int off = 0; off < 8; off++) { |
| for (int sz1 = 0; sz1 < 3; sz1++) { |
| for (int off2 = 0; off2 < accesssize(sz1); off2++) { |
| for (int rw = 0; rw < 2; rw++) { |
| // printf("thr=%d off=%d sz1=%d off2=%d rw=%d p=%p\n", |
| // main, off, sz1, off2, rw, obj); |
| access1(main, off, sz1, off2, rw, (char*)obj); |
| obj += 2; |
| } |
| } |
| } |
| } |
| } |
| |
| void *Thread(void *p) { |
| (void)p; |
| sleep(1); |
| Test(false); |
| return 0; |
| } |
| |
| int main() { |
| pthread_t th; |
| pthread_create(&th, 0, Thread, 0); |
| Test(true); |
| pthread_join(th, 0); |
| } |
| |
| // CHECK: WARNING: ThreadSanitizer: data race |
| // CHECK: ThreadSanitizer: reported 224 warnings |