/*
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 few macros useful for implementing
unit-tests for data race detection tools.
*/
#ifndef TEST_UTILS_H__
#define TEST_UTILS_H__
// This test must not include any other file specific to threading library,
// everything should be inside THREAD_WRAPPERS.
#ifndef THREAD_WRAPPERS
# define THREAD_WRAPPERS "thread_wrappers.h"
#endif
#include THREAD_WRAPPERS
#ifndef NEEDS_SEPERATE_RW_LOCK
#define RWLock Mutex // Mutex does work as an rw-lock.
#define WriterLockScoped MutexLock
#define ReaderLockScoped ReaderMutexLock
#endif // !NEEDS_SEPERATE_RW_LOCK
static bool ArgIsOne(int *arg) { return *arg == 1; };
static bool ArgIsZero(int *arg) { return *arg == 0; };
static bool ArgIsTrue(bool *arg) { return *arg == true; };
// If run under ThreadSanitizerQuery, this function is replaced by the tool
// and a non-NULL string is returned. See the usage below.
extern "C" const char *ThreadSanitizerQuery(const char *query);
// Apply ANNOTATE_EXPECT_RACE only if running under ThreadSanitizer.
#define ANNOTATE_EXPECT_RACE_FOR_TSAN(mem, descr) \
do {\
if (ThreadSanitizerQuery("") != NULL) {\
ANNOTATE_EXPECT_RACE(mem, descr); \
} \
} while(0)\
inline bool ThreadSanitizerQueryMatch(const char *query, const char *expected_answer) {
const char *answer = ThreadSanitizerQuery(query);
if (answer == NULL) {
// Not running under ThreadSanitizer at all.
return false;
}
return string(answer) == expected_answer;
}
inline bool Tsan_PureHappensBefore() {
static bool ret = ThreadSanitizerQueryMatch("pure_happens_before", "1");
return ret;
}
inline bool Tsan_RaceVerifier() {
static bool ret = ThreadSanitizerQueryMatch("race_verifier", "1");
return ret;
}
// An array of threads. Create/start/join all elements at once.
class MyThreadArray {
public:
static const int kSize = 5;
typedef void (*F) (void);
MyThreadArray(F f1, F f2 = NULL, F f3 = NULL, F f4 = NULL, F f5 = NULL) {
ar_[0] = new MyThread(f1);
ar_[1] = f2 ? new MyThread(f2) : NULL;
ar_[2] = f3 ? new MyThread(f3) : NULL;
ar_[3] = f4 ? new MyThread(f4) : NULL;
ar_[4] = f5 ? new MyThread(f5) : NULL;
}
void Start() {
for(int i = 0; i < kSize; i++) {
if(ar_[i]) {
ar_[i]->Start();
usleep(10);
}
}
}
void Join() {
for(int i = 0; i < kSize; i++) {
if(ar_[i]) {
ar_[i]->Join();
}
}
}
~MyThreadArray() {
for(int i = 0; i < kSize; i++) {
delete ar_[i];
}
}
private:
MyThread *ar_[kSize];
};
// This class does not implement a signal-wait synchronization
// primitive, even if it looks like one. Its purpose is to enforce an
// order of execution of threads in unit tests in a way that is
// invisible to ThreadSanitizer and similar tools. It lacks memory
// barriers, therefore it only works reliably if there is a real
// synchronization primitive before signal() or after wait().
class StealthNotification {
public:
StealthNotification() : flag_(0) {}
void signal() {
ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN();
CHECK(!flag_);
flag_ = 1;
ANNOTATE_IGNORE_READS_AND_WRITES_END();
}
void wait() {
while (!flag_) {
#ifdef WIN32
usleep(1000);
#else
sched_yield();
#endif
}
}
private:
volatile int flag_;
};
#endif // TEST_UTILS_H__
// End {{{1
// vim:shiftwidth=2:softtabstop=2:expandtab:foldmethod=marker