/*
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