/* This file is part of Valgrind, a dynamic binary instrumentation framework. Copyright (C) 2008-2008 Google Inc opensource@google.com This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The GNU General Public License is contained in the file COPYING. */ /* Author: Konstantin Serebryany <opensource@google.com> This file contains a set of unit tests for a data race detection tool. These tests can be compiled with pthreads (default) or with any other library that supports threads, locks, cond vars, etc. */ #ifdef WIN32 #error "Don't build this file on Windows!" #endif #include <fcntl.h> #include <fenv.h> #include <queue> #include <signal.h> #include <stdlib.h> #include <string> #include <vector> #include <unistd.h> #ifdef OS_linux # include <sys/epoll.h> #endif // OS_linux #include "test_utils.h" #include <gtest/gtest.h> #include "gtest_fixture_injection.h" static CondVar CV; static int COND = 0; // test11: FP. Synchronization via CondVar, 2 workers. {{{1 // This test is properly synchronized, but currently (Dec 2007) // helgrind reports a false positive. // // Parent: Worker1, Worker2: // 1. Start(workers) a. read(GLOB) // 2. MU.Lock() b. MU.Lock() // 3. while(COND != 2) /-------- c. CV.Signal() // CV.Wait(&MU) <-------/ d. MU.Unlock() // 4. MU.Unlock() // 5. write(GLOB) // namespace test11 { int GLOB = 0; Mutex MU; void Worker() { usleep(200000); CHECK(GLOB != 777); MU.Lock(); COND++; CV.Signal(); MU.Unlock(); } void Parent() { COND = 0; MyThreadArray t(Worker, Worker); t.Start(); MU.Lock(); while(COND != 2) { CV.Wait(&MU); } MU.Unlock(); GLOB = 2; t.Join(); } TEST(NegativeTests, test11) { // ANNOTATE_EXPECT_RACE(&GLOB, "test11. FP. Fixed by MSMProp1."); printf("test11: negative\n"); Parent(); printf("\tGLOB=%d\n", GLOB); } } // namespace test11 // test75: TN. Test for sem_post, sem_wait, sem_trywait. {{{1 namespace test75 { int GLOB = 0; sem_t sem[2]; void Poster() { GLOB = 1; sem_post(&sem[0]); sem_post(&sem[1]); } void Waiter() { sem_wait(&sem[0]); CHECK(GLOB==1); } void TryWaiter() { usleep(500000); sem_trywait(&sem[1]); CHECK(GLOB==1); } TEST(NegativeTests, test75) { #ifndef NO_UNNAMED_SEM sem_init(&sem[0], 0, 0); sem_init(&sem[1], 0, 0); printf("test75: negative\n"); { MyThreadArray t(Poster, Waiter); t.Start(); t.Join(); } GLOB = 2; { MyThreadArray t(Poster, TryWaiter); t.Start(); t.Join(); } printf("\tGLOB=%d\n", GLOB); sem_destroy(&sem[0]); sem_destroy(&sem[1]); #endif // NO_UNNAMED_SEM } } // namespace test75 // test98: Synchronization via read/write (or send/recv). {{{1 namespace test98 { // The synchronization here is done by a pair of read/write calls // that create a happens-before arc. Same may be done with send/recv. // Such synchronization is quite unusual in real programs // (why would one synchronizae via a file or socket?), but // quite possible in unittests where one threads runs for producer // and one for consumer. // // A race detector has to create a happens-before arcs for // {read,send}->{write,recv} even if the file descriptors are different. // int GLOB = 0; int fd_out = -1; int fd_in = -1; void Writer() { usleep(1000); GLOB = 1; const char *str = "Hey there!\n"; const int size = strlen(str) + 1; CHECK(size == write(fd_out, str, size)); } void Reader() { char buff[100]; while (read(fd_in, buff, 100) == 0) sleep(1); printf("read: %s\n", buff); GLOB = 2; } #ifndef __APPLE__ // Tsan for Mac OS is missing the unlink() syscall handler. // TODO(glider): add the syscall handler to Valgrind. TEST(NegativeTests, test98) { printf("test98: negative, synchronization via I/O\n"); char in_name[100]; char out_name[100]; // we open two files, on for reading and one for writing, // but the files are actually the same (symlinked). sprintf(in_name, "/tmp/racecheck_unittest_in.%d", getpid()); sprintf(out_name, "/tmp/racecheck_unittest_out.%d", getpid()); fd_out = creat(out_name, O_WRONLY | S_IRWXU); CHECK(0 == symlink(out_name, in_name)); fd_in = open(in_name, 0, O_RDONLY); CHECK(fd_out >= 0); CHECK(fd_in >= 0); MyThreadArray t(Writer, Reader); t.Start(); t.Join(); printf("\tGLOB=%d\n", GLOB); // cleanup close(fd_in); close(fd_out); unlink(in_name); unlink(out_name); } #endif // __APPLE__ } // namespace test98 namespace NegativeTests_PthreadOnce { // {{{1 int *GLOB = NULL; static pthread_once_t once = PTHREAD_ONCE_INIT; void Init() { GLOB = new int; ANNOTATE_TRACE_MEMORY(GLOB); *GLOB = 777; } void Worker0() { pthread_once(&once, Init); } void Worker1() { usleep(100000); pthread_once(&once, Init); CHECK(*GLOB == 777); } TEST(NegativeTests, PthreadOnceTest) { MyThreadArray t(Worker0, Worker1, Worker1, Worker1); t.Start(); t.Join(); printf("\tGLOB=%d\n", *GLOB); } } // namespace // test110: TP. Simple races with stack, global and heap objects. {{{1 namespace test110 { int GLOB = 0; static int STATIC; char *STACK = 0; int *MALLOC; int *CALLOC; int *REALLOC; int *VALLOC; int *PVALLOC; int *MEMALIGN; int *POSIX_MEMALIGN; int *MMAP; int *NEW; int *NEW_ARR; void Worker() { GLOB++; STATIC++; (*STACK)++; (*MALLOC)++; (*CALLOC)++; (*REALLOC)++; (*VALLOC)++; (*PVALLOC)++; (*MEMALIGN)++; (*POSIX_MEMALIGN)++; (*MMAP)++; (*NEW)++; (*NEW_ARR)++; } TEST(PositiveTests, test110) { printf("test110: positive (race on a stack object)\n"); char x = 0; STACK = &x; MALLOC = (int*)malloc(sizeof(int)); CALLOC = (int*)calloc(1, sizeof(int)); REALLOC = (int*)realloc(NULL, sizeof(int)); VALLOC = (int*)valloc(sizeof(int)); PVALLOC = (int*)valloc(sizeof(int)); // TODO: pvalloc breaks helgrind. MEMALIGN = (int*)memalign(64, sizeof(int)); CHECK(0 == posix_memalign((void**)&POSIX_MEMALIGN, 64, sizeof(int))); MMAP = (int*)mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); NEW = new int; NEW_ARR = new int[10]; ANNOTATE_EXPECT_RACE(STACK, "real race on stack object"); ANNOTATE_EXPECT_RACE(&GLOB, "real race on global object"); ANNOTATE_EXPECT_RACE(&STATIC, "real race on a static global object"); ANNOTATE_EXPECT_RACE(MALLOC, "real race on a malloc-ed object"); ANNOTATE_EXPECT_RACE(CALLOC, "real race on a calloc-ed object"); ANNOTATE_EXPECT_RACE(REALLOC, "real race on a realloc-ed object"); ANNOTATE_EXPECT_RACE(VALLOC, "real race on a valloc-ed object"); ANNOTATE_EXPECT_RACE(PVALLOC, "real race on a pvalloc-ed object"); ANNOTATE_EXPECT_RACE(MEMALIGN, "real race on a memalign-ed object"); ANNOTATE_EXPECT_RACE(POSIX_MEMALIGN, "real race on a posix_memalign-ed object"); ANNOTATE_EXPECT_RACE(MMAP, "real race on a mmap-ed object"); ANNOTATE_EXPECT_RACE(NEW, "real race on a new-ed object"); ANNOTATE_EXPECT_RACE(NEW_ARR, "real race on a new[]-ed object"); MyThreadArray t(Worker, Worker, Worker); t.Start(); t.Join(); printf("\tSTACK=%d\n", *STACK); CHECK(GLOB <= 3); CHECK(STATIC <= 3); free(MALLOC); free(CALLOC); free(REALLOC); free(VALLOC); free(PVALLOC); free(MEMALIGN); free(POSIX_MEMALIGN); munmap(MMAP, sizeof(int)); delete NEW; delete [] NEW_ARR; } } // namespace test110 // test115: TN. sem_open. {{{1 namespace test115 { int tid = 0; Mutex mu; const char *kSemName = "drt-test-sem"; int GLOB = 0; sem_t *DoSemOpen() { // TODO: there is some race report inside sem_open // for which suppressions do not work... (???) ANNOTATE_IGNORE_WRITES_BEGIN(); sem_t *sem = sem_open(kSemName, O_CREAT, 0600, 3); ANNOTATE_IGNORE_WRITES_END(); return sem; } void Worker() { mu.Lock(); int my_tid = tid++; mu.Unlock(); if (my_tid == 0) { GLOB = 1; } // if the detector observes a happens-before arc between // sem_open and sem_wait, it will be silent. sem_t *sem = DoSemOpen(); usleep(100000); CHECK(sem != SEM_FAILED); CHECK(sem_wait(sem) == 0); if (my_tid > 0) { CHECK(GLOB == 1); } } #ifndef __APPLE__ /* This test is disabled for Darwin because of the tricky implementation of * sem_open on that platform: subsequent attempts to open an existing semafore * create new ones. */ TEST(NegativeTests, test115) { printf("test115: stab (sem_open())\n"); // just check that sem_open is not completely broken sem_unlink(kSemName); sem_t* sem = DoSemOpen(); CHECK(sem != SEM_FAILED); CHECK(sem_wait(sem) == 0); sem_unlink(kSemName); // check that sem_open and sem_wait create a happens-before arc. MyThreadArray t(Worker, Worker, Worker); t.Start(); t.Join(); // clean up sem_unlink(kSemName); } #endif // __APPLE__ } // namespace test115 // test122 TP: Simple test with RWLock {{{1 namespace test122 { int VAR1 = 0; int VAR2 = 0; RWLock mu; void WriteWhileHoldingReaderLock(int *p) { usleep(100000); ReaderLockScoped lock(&mu); // Reader lock for writing. -- bug. (*p)++; } void CorrectWrite(int *p) { WriterLockScoped lock(&mu); (*p)++; } void Thread1() { WriteWhileHoldingReaderLock(&VAR1); } void Thread2() { CorrectWrite(&VAR1); } void Thread3() { CorrectWrite(&VAR2); } void Thread4() { WriteWhileHoldingReaderLock(&VAR2); } TEST(PositiveTests, test122) { printf("test122: positive (rw-lock)\n"); VAR1 = 0; VAR2 = 0; ANNOTATE_TRACE_MEMORY(&VAR1); ANNOTATE_TRACE_MEMORY(&VAR2); if (!Tsan_PureHappensBefore()) { ANNOTATE_EXPECT_RACE_FOR_TSAN(&VAR1, "test122. TP. ReaderLock-ed while writing"); ANNOTATE_EXPECT_RACE_FOR_TSAN(&VAR2, "test122. TP. ReaderLock-ed while writing"); } MyThreadArray t(Thread1, Thread2, Thread3, Thread4); t.Start(); t.Join(); } } // namespace test122 // test125 TN: Backwards lock (annotated). {{{1 namespace test125 { // This test uses "Backwards mutex" locking protocol. // We take a *reader* lock when writing to a per-thread data // (GLOB[thread_num]) and we take a *writer* lock when we // are reading from the entire array at once. // // Such locking protocol is not understood by ThreadSanitizer's // hybrid state machine. So, you either have to use a pure-happens-before // detector ("tsan --pure-happens-before") or apply pure happens-before mode // to this particular lock by using ANNOTATE_PURE_HAPPENS_BEFORE_MUTEX(&mu). const int n_threads = 3; RWLock mu; int GLOB[n_threads]; int adder_num; // updated atomically. void Adder() { int my_num = AtomicIncrement(&adder_num, 1) - 1; CHECK(my_num >= 0); CHECK(my_num < n_threads); ReaderLockScoped lock(&mu); GLOB[my_num]++; } void Aggregator() { int sum = 0; { WriterLockScoped lock(&mu); for (int i = 0; i < n_threads; i++) { sum += GLOB[i]; } } printf("sum=%d\n", sum); } TEST(NegativeTests, test125) { printf("test125: negative\n"); ANNOTATE_PURE_HAPPENS_BEFORE_MUTEX(&mu); // run Adders, then Aggregator adder_num = 0; { MyThreadArray t(Adder, Adder, Adder, Aggregator); t.Start(); t.Join(); } // Run Aggregator first. adder_num = 0; { MyThreadArray t(Aggregator, Adder, Adder, Adder); t.Start(); t.Join(); } } } // namespace test125 namespace MmapTest { // {{{1 const int kMmapSize = 65536; void SubWorker() { for (int i = 0; i < 500; i++) { int *ptr = (int*)mmap(NULL, kMmapSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); *ptr = 42; munmap(ptr, kMmapSize); } } void Worker1() { MyThreadArray t(SubWorker, SubWorker, SubWorker, SubWorker); t.Start(); t.Join(); } void Worker() { MyThreadArray t(Worker1, Worker1, Worker1, Worker1); t.Start(); t.Join(); } TEST(NegativeTests, MmapTest) { MyThreadArray t(Worker, Worker, Worker, Worker); t.Start(); t.Join(); } } // namespace // A regression test for mmap/munmap handling in Pin. // If the tool misses munmap() calls it may report a false positive if two // threads map the same memory region. namespace MmapRegressionTest { // {{{1 const int kMmapSize = 65536; const uintptr_t kStartAddress = 0x10000; StealthNotification n1; void Worker() { int *ptr = (int*)mmap((void*)kStartAddress, kMmapSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); *ptr = 42; munmap(ptr, kMmapSize); } TEST(NegativeTests, MmapRegressionTest) { MyThreadArray t(Worker, Worker); t.Start(); t.Join(); } } // namespace // test136. Unlock twice. {{{1 namespace test136 { TEST(LockTests, UnlockTwice) { pthread_mutexattr_t attr; CHECK(0 == pthread_mutexattr_init(&attr)); CHECK(0 == pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK)); pthread_mutex_t mu; CHECK(0 == pthread_mutex_init(&mu, &attr)); CHECK(0 == pthread_mutex_lock(&mu)); CHECK(0 == pthread_mutex_unlock(&mu)); int ret_unlock = pthread_mutex_unlock(&mu); // unlocking twice. int ret_destroy = pthread_mutex_destroy(&mu); printf(" pthread_mutex_unlock returned %d\n", ret_unlock); printf(" pthread_mutex_destroy returned %d\n", ret_destroy); } } // namespace test136 // test141 FP. unlink/fopen, rmdir/opendir. {{{1 namespace test141 { int GLOB1 = 0, GLOB2 = 0; char *dir_name = NULL, *filename = NULL; void Waker1() { usleep(100000); GLOB1 = 1; // Write // unlink deletes a file 'filename' // which exits spin-loop in Waiter1(). printf(" Deleting file...\n"); CHECK(unlink(filename) == 0); } void Waiter1() { FILE *tmp; while ((tmp = fopen(filename, "r")) != NULL) { fclose(tmp); usleep(10000); } printf(" ...file has been deleted\n"); GLOB1 = 2; // Write } void Waker2() { usleep(100000); GLOB2 = 1; // Write // rmdir deletes a directory 'dir_name' // which exit spin-loop in Waker(). printf(" Deleting directory...\n"); CHECK(rmdir(dir_name) == 0); } void Waiter2() { DIR *tmp; while ((tmp = opendir(dir_name)) != NULL) { closedir(tmp); usleep(10000); } printf(" ...directory has been deleted\n"); GLOB2 = 2; } TEST(NegativeTests, test141) { printf("test141: FP. unlink/fopen, rmdir/opendir.\n"); dir_name = strdup("/tmp/tsan-XXXXXX"); CHECK(mkdtemp(dir_name) != 0); filename = strdup((std::string() + dir_name + "/XXXXXX").c_str()); const int fd = mkstemp(filename); CHECK(fd >= 0); close(fd); MyThreadArray mta1(Waker1, Waiter1); mta1.Start(); mta1.Join(); MyThreadArray mta2(Waker2, Waiter2); mta2.Start(); mta2.Join(); free(filename); filename = 0; free(dir_name); dir_name = 0; } } // namespace test141 // test146: TP. Unit test for RWLock::TryLock and RWLock::ReaderTryLock. {{{1 namespace test146 { // Worker1 locks the globals for writing for a long time. // Worker2 tries to write to globals twice: without a writer lock and with it. // Worker3 tries to read from globals twice: without a reader lock and with it. int GLOB1 = 0; char padding1[64]; int GLOB2 = 0; char padding2[64]; int GLOB3 = 0; char padding3[64]; int GLOB4 = 0; RWLock MU; StealthNotification n1, n2, n3, n4, n5; void Worker1() { MU.Lock(); GLOB1 = 1; GLOB2 = 1; GLOB3 = 1; GLOB4 = 1; n1.signal(); n2.wait(); n3.wait(); MU.Unlock(); n4.signal(); } void Worker2() { n1.wait(); if (MU.TryLock()) CHECK(0); else GLOB1 = 2; n2.signal(); n5.wait(); if (MU.TryLock()) { GLOB2 = 2; MU.Unlock(); } else { CHECK(0); } } void Worker3() { n1.wait(); if (MU.ReaderTryLock()) CHECK(0); else printf("\treading GLOB3: %d\n", GLOB3); n3.signal(); n4.wait(); if (MU.ReaderTryLock()) { printf("\treading GLOB4: %d\n", GLOB4); MU.ReaderUnlock(); } else { CHECK(0); } n5.signal(); } TEST(PositiveTests, test146) { ANNOTATE_EXPECT_RACE_FOR_TSAN(&GLOB1, "test146. TP: a data race on GLOB1."); ANNOTATE_EXPECT_RACE_FOR_TSAN(&GLOB3, "test146. TP: a data race on GLOB3."); ANNOTATE_TRACE_MEMORY(&GLOB1); ANNOTATE_TRACE_MEMORY(&GLOB2); ANNOTATE_TRACE_MEMORY(&GLOB3); ANNOTATE_TRACE_MEMORY(&GLOB4); printf("test146: positive\n"); MyThreadArray t(Worker1, Worker2, Worker3); t.Start(); t.Join(); printf("\tGLOB1=%d\n", GLOB1); printf("\tGLOB2=%d\n", GLOB2); printf("\tGLOB3=%d\n", GLOB3); printf("\tGLOB4=%d\n", GLOB4); } } // namespace test146 namespace PositiveTests_CyclicBarrierTest { // {{{1 #ifndef NO_BARRIER // regression test for correct support of cyclic barrier. // This test was suggested by Julian Seward. // There is a race on L here between a1 and b1, // but a naive support of barrier makes us miss this race. pthread_barrier_t B; int L; // A1/A2: write L, then wait for barrier, then sleep void a1() { L = 1; pthread_barrier_wait(&B); sleep(1); } void a2() { pthread_barrier_wait(&B); sleep(1); } // B1/B2: sleep, wait for barrier, then write L void b1() { sleep(1); pthread_barrier_wait(&B); L = 1; } void b2() { sleep(1); pthread_barrier_wait(&B); } TEST(PositiveTests, CyclicBarrierTest) { ANNOTATE_EXPECT_RACE_FOR_TSAN(&L, "real race, may be hidden" " by incorrect implementation of barrier"); pthread_barrier_init(&B, NULL, 3); MyThreadArray t1(a1, a2, a2), t2(b1, b2, b2); t1.Start(); t2.Start(); t1.Join(); t2.Join(); } int *G = NULL; void Worker() { pthread_barrier_wait(&B); (*G) = 1; pthread_barrier_wait(&B); } TEST(PositiveTests, CyclicBarrierTwoCallsTest) { pthread_barrier_init(&B, NULL, 2); G = new int(0); ANNOTATE_TRACE_MEMORY(G); ANNOTATE_EXPECT_RACE_FOR_TSAN(G, "real race, may be hidden" " by incorrect implementation of barrier"); MyThreadArray t1(Worker, Worker); t1.Start(); t1.Join(); CHECK(*G == 1); delete G; } #endif // NO_BARRIER } // namespace TEST(NegativeTests, Mmap84GTest) { // {{{1 #ifdef ARCH_amd64 #ifdef OS_linux // test that we can mmap 84G and can do it fast. size_t size = (1ULL << 32) * 21; // 21 * 4G void *mem_ptr = mmap((void *) 0, size, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE, -1, (off_t) 0); printf("res=%p\n", mem_ptr); #endif #endif } namespace NegativeTests_PthreadCreateFailureTest { // {{{1 #ifdef OS_linux void* ThreadRoutine(void *) { return NULL; } TEST(NegativeTests, PthreadCreateFailureTest) { pthread_attr_t attributes; pthread_attr_init(&attributes); pthread_attr_setstacksize(&attributes, -1); pthread_t handle; CHECK(pthread_create(&handle, &attributes, ThreadRoutine, NULL) != 0); pthread_attr_destroy(&attributes); } #endif // OS_linux } // namespace NegativeTests_PthreadCreateFailureTest namespace Signals { // {{{1 typedef void (*Sigaction)(int, siginfo_t *, void *); int GLOB = 0; static void EnableSigprof(Sigaction SignalHandler) { struct sigaction sa; sa.sa_sigaction = SignalHandler; sa.sa_flags = SA_RESTART | SA_SIGINFO; sigemptyset(&sa.sa_mask); if (sigaction(SIGPROF, &sa, NULL) != 0) { perror("sigaction"); abort(); } struct itimerval timer; timer.it_interval.tv_sec = 0; timer.it_interval.tv_usec = 1000000 / 10000; timer.it_value = timer.it_interval; if (setitimer(ITIMER_PROF, &timer, 0) != 0) { perror("setitimer"); abort(); } } static void DisableSigprof() { // Disable the profiling timer. struct itimerval timer; timer.it_interval.tv_sec = 0; timer.it_interval.tv_usec = 0; timer.it_value = timer.it_interval; if (setitimer(ITIMER_PROF, &timer, 0) != 0) { perror("setitimer"); abort(); } // Ignore SIGPROFs from now on. struct sigaction sa; sa.sa_handler = SIG_IGN; sa.sa_flags = SA_RESTART | SA_SIGINFO; sigemptyset(&sa.sa_mask); if (sigaction(SIGPROF, &sa, NULL) != 0) { perror("sigaction"); abort(); } } void MallocTestWorker() { for (int i = 0; i < 100000; i++) { void *x = malloc((i % 64) + 1); free (x); } } // Regression test for // http://code.google.com/p/data-race-test/issues/detail?id=13 . // Make sure that locking events are handled in signal handlers. // // For some reason, invoking the signal handlers causes deadlocks on Mac OS. #ifndef __APPLE__ Mutex mu; void SignalHandlerWithMutex(int, siginfo_t*, void*) { mu.Lock(); GLOB++; mu.Unlock(); } TEST(Signals, SignalsAndMallocTestWithMutex) { EnableSigprof(SignalHandlerWithMutex); MyThreadArray t(MallocTestWorker, MallocTestWorker, MallocTestWorker); t.Start(); t.Join(); printf("\tGLOB=%d\n", GLOB); DisableSigprof(); } #endif // Another regression test for // http://code.google.com/p/data-race-test/issues/detail?id=13 . // Make sure that locking events are handled in signal handlers. SpinLock sl; void SignalHandlerWithSpinlock(int, siginfo_t*, void*) { sl.Lock(); GLOB++; sl.Unlock(); } TEST(Signals, DISABLED_SignalsAndMallocTestWithSpinlock) { EnableSigprof(SignalHandlerWithSpinlock); MyThreadArray t(MallocTestWorker, MallocTestWorker, MallocTestWorker); t.Start(); t.Join(); printf("\tGLOB=%d\n", GLOB); DisableSigprof(); } // Regression test for // http://code.google.com/p/data-race-test/issues/detail?id=14. static void WaitTestSignalHandler(int, siginfo_t*, void*) { ANNOTATE_HAPPENS_AFTER((void*)0x1234); } void WaitTestWorker() { for (int i = 0; i < 1000000; i++) { ANNOTATE_HAPPENS_AFTER((void*)0x1234); } } TEST(Signals, SignalsAndWaitTest) { EnableSigprof(WaitTestSignalHandler); MyThreadArray t(WaitTestWorker, WaitTestWorker, WaitTestWorker); t.Start(); t.Join(); DisableSigprof(); } #ifndef __APPLE__ // this test crashes on Mac in debug TSan build, see // http://code.google.com/p/data-race-test/issues/detail?id=47 pid_t child_pid = 0; void child_handler(int signum) { if (signum == SIGCHLD && child_pid == 0) { printf("Whoops, PID shouldn't be 0!\n"); } } TEST(Signals, PositiveTests_RaceInSignal) { // Currently the data race on child_pid can't be found, // see http://code.google.com/p/data-race-test/issues/detail?id=46 //ANNOTATE_EXPECT_RACE(&child_pid, "Race on pid: fork vs signal handler"); signal(SIGCHLD, child_handler); child_pid = fork(); if (child_pid == 0) { // in child exit(0); } wait(NULL); } #endif // __APPLE__ } // namespace; TEST(WeirdSizesTests, FegetenvTest) { // http://code.google.com/p/data-race-test/issues/detail?id=36 fenv_t tmp; if (fegetenv(&tmp) != 0) FAIL() << "fegetenv failed"; } namespace NegativeTests_epoll { // {{{1 #ifdef OS_linux int GLOB; // Currently, ThreadSanitizer should create hb arcs between // epoll_ctl and epoll_wait regardless of the parameters. Check that. void Worker1() { GLOB++; struct epoll_event event; epoll_ctl(0, 0, 0, &event); } void Worker2() { struct epoll_event event; epoll_wait(0, &event, 0, 0); GLOB++; } TEST(NegativeTests,epollTest) { MyThreadArray mta(Worker1, Worker2); mta.Start(); mta.Join(); } #endif // OS_linux } namespace NegativeTests_LockfTest { // {{{1 class ShmMutex { public: ShmMutex() : fd_(-1) { } void set_fd(int fd) { CHECK(fd_ == -1); fd_ = fd; } void Lock() { LockOrUnlockInternal(true); } void Unlock() { LockOrUnlockInternal(false); } private: void LockOrUnlockInternal(bool lock) { CHECK(fd_ >= 0); while (lockf(fd_, lock ? F_LOCK : F_ULOCK, 0) < 0) { if (errno == EINTR) { continue; } else if (errno == ENOLCK) { usleep(5000); continue; } CHECK(0); } } int fd_; } mu; int GLOB; void Worker() { mu.Lock(); GLOB++; mu.Unlock(); } TEST(NegativeTests,DISABLED_LockfTest) { mu.set_fd(1 /* stdout */); MyThreadArray mta(Worker, Worker); mta.Start(); mta.Join(); } } namespace PositiveTests_LockThenNoLock { // {{{1 // Regression test for a bug fixed by r2312 int GLOB; pthread_mutex_t mu; StealthNotification n1, n2; void Worker1() { pthread_mutex_lock(&mu); GLOB = 1; pthread_mutex_unlock(&mu); n1.signal(); n2.wait(); GLOB = 2; } void Worker2() { pthread_mutex_lock(&mu); GLOB = 3; pthread_mutex_unlock(&mu); n2.signal(); n1.wait(); GLOB = 4; } TEST(PositiveTests, LockThenNoLock) { pthread_mutex_init(&mu, NULL); ANNOTATE_TRACE_MEMORY(&GLOB); ANNOTATE_EXPECT_RACE(&GLOB, "race"); ANNOTATE_NOT_HAPPENS_BEFORE_MUTEX(&mu); MyThreadArray t(Worker1, Worker2); t.Start(); t.Join(); pthread_mutex_destroy(&mu); } } // namespace #ifdef __APPLE__ namespace NegativeTests_PthreadCondWaitRelativeNp { // {{{1 int GLOB = 0; pthread_mutex_t mu; pthread_cond_t cv; void Waiter() { struct timespec tv = {1000, 1000}; pthread_mutex_lock(&mu); pthread_cond_timedwait_relative_np(&cv, &mu, &tv); GLOB = 2; pthread_mutex_unlock(&mu); } void Waker() { pthread_mutex_lock(&mu); GLOB = 1; pthread_cond_signal(&cv); pthread_mutex_unlock(&mu); } TEST(NegativeTests, PthreadCondWaitRelativeNpTest) { pthread_mutex_init(&mu, NULL); pthread_cond_init(&cv, NULL); MyThreadArray t(Waiter, Waker); t.Start(); t.Join(); pthread_mutex_destroy(&mu); pthread_cond_destroy(&cv); } } // namespace #endif // __APPLE__ namespace PositiveTests_RWLockVsRWLockTest { // {{{1 // Test that reader lock/unlock do not create a hb-arc. RWLock mu; int GLOB; StealthNotification n; void Thread1() { GLOB = 1; mu.ReaderLock(); mu.ReaderUnlock(); n.signal(); } void Thread2() { n.wait(); mu.ReaderLock(); mu.ReaderUnlock(); GLOB = 1; } TEST(PositiveTests, RWLockVsRWLockTest) { ANNOTATE_PURE_HAPPENS_BEFORE_MUTEX(&mu); ANNOTATE_EXPECT_RACE(&GLOB, "rwunlock/rwlock is not a hb-arc"); MyThreadArray t(Thread1, Thread2); t.Start(); t.Join(); } } // namespace namespace TSDTests { // Test the support for libpthread TSD destructors. pthread_key_t key; const int kInitialValue = 0xfeedface; int tsd_array[2]; void Destructor(void *ptr) { int *write = (int*) ptr; *write = kInitialValue; } void DoWork(int index) { int *value = &(tsd_array[index]); *value = 42; pthread_setspecific(key, value); int *read = (int*) pthread_getspecific(key); CHECK(read == value); } void Worker0() { DoWork(0); } void Worker1() { DoWork(1); } TEST(TSDTests, TSDDestructorTest) { pthread_key_create(&key, Destructor); MyThreadArray t(Worker0, Worker1); t.Start(); t.Join(); for (int i = 0; i < 2; ++i) { CHECK(tsd_array[i] == kInitialValue); } } } // End {{{1 // vim:shiftwidth=2:softtabstop=2:expandtab:foldmethod=marker