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