/* This file is part of ThreadSanitizer, a dynamic data race detector. Copyright (C) 2008-2009 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> // // Here we define few simple classes that wrap pthread primitives. // // If one needs to test ThreadSanitizer's support for another threading library, // he/she can create a copy of this file and replace pthread_ calls // with appropriate calls to his/her library. // // Note, that some of the methods defined here are annotated with // ANNOTATE_* macros defined in dynamic_annotations.h. // // DISCLAIMER: the classes defined in this header file // are NOT intended for general use -- only for unit tests. #ifndef THREAD_WRAPPERS_PTHREADS_H_ #define THREAD_WRAPPERS_PTHREADS_H_ #include <dirent.h> #include <errno.h> #include <pthread.h> #include <semaphore.h> #include <stdlib.h> #include <stdint.h> #include <sys/mman.h> // mmap #include <sys/time.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #define NOINLINE __attribute__ ((noinline)) #define ALIGNED(X) __attribute__ ((aligned (X))) // This constant is true if malloc() uses mutex on your platform as this may // introduce a happens-before arc for a pure happens-before race detector. static const bool kMallocUsesMutex = false; #ifndef __APPLE__ // Linux #include <malloc.h> // memalign #ifdef ANDROID #define NO_BARRIER #define NO_SPINLOCK #endif // Older Android toolchain does not support atomic builtins. #if !defined(ANDROID) || defined(__ANDROID__) static int AtomicIncrement(volatile int *value, int increment) { return __sync_add_and_fetch(value, increment); } #else static int AtomicIncrement(volatile int *value, int increment) { static pthread_mutex_t mu = PTHREAD_MUTEX_INITIALIZER; ANNOTATE_NOT_HAPPENS_BEFORE_MUTEX(&mu); pthread_mutex_lock(&mu); int result = *value += increment; pthread_mutex_unlock(&mu); return result; } #endif #ifdef ANDROID #undef TLS #else #define TLS __thread #endif #else // Mac OS X #include <libkern/OSAtomic.h> #define NO_BARRIER #define NO_UNNAMED_SEM #undef TLS #define NO_SPINLOCK static int AtomicIncrement(volatile int *value, int increment) { return OSAtomicAdd32(increment, value); } // TODO(timurrrr) this is a hack #define memalign(A,B) malloc(B) #ifndef OS_darwin_10 // TODO(timurrrr) this is a hack static int posix_memalign(void **out, size_t al, size_t size) { *out = memalign(al, size); return (*out == 0); } #endif #endif static int GetTimeInMs() { struct timeval now; gettimeofday(&now, NULL); return (int)(now.tv_sec * 1000 + now.tv_usec / 1000); } /// Copy tv to ts adding offset in milliseconds. static inline void timeval2timespec(timeval *const tv, timespec *ts, int64_t offset_milli) { const int64_t ten_9 = 1000000000LL; const int64_t ten_6 = 1000000LL; const int64_t ten_3 = 1000LL; int64_t now_nsec = (int64_t)tv->tv_sec * ten_9; now_nsec += (int64_t)tv->tv_usec * ten_3; int64_t then_nsec = now_nsec + offset_milli * ten_6; ts->tv_sec = then_nsec / ten_9; ts->tv_nsec = then_nsec % ten_9; } /// Wrapper for pthread_mutex_t. /// /// pthread_mutex_t is *not* a reader-writer lock, /// so the methods like ReaderLock() aren't really reader locks. /// We can not use pthread_rwlock_t because it /// does not work with pthread_cond_t. /// /// TODO: We still need to test reader locks with this class. /// Implement a mode where pthread_rwlock_t will be used /// instead of pthread_mutex_t (only when not used with CondVar or LockWhen). /// class Mutex { friend class CondVar; public: Mutex() { CHECK(0 == pthread_mutex_init(&mu_, NULL)); CHECK(0 == pthread_cond_init(&cv_, NULL)); signal_at_unlock_ = false; } ~Mutex() { CHECK(0 == pthread_cond_destroy(&cv_)); CHECK(0 == pthread_mutex_destroy(&mu_)); } void Lock() { CHECK(0 == pthread_mutex_lock(&mu_));} bool TryLock() { return (0 == pthread_mutex_trylock(&mu_));} void Unlock() { ANNOTATE_HAPPENS_BEFORE(this); if (signal_at_unlock_) { CHECK(0 == pthread_cond_signal(&cv_)); } CHECK(0 == pthread_mutex_unlock(&mu_)); } void ReaderLock() { Lock(); } bool ReaderTryLock() { return TryLock();} void ReaderUnlock() { Unlock(); } void LockWhen(Condition cond) { Lock(); WaitLoop(cond); } void ReaderLockWhen(Condition cond) { Lock(); WaitLoop(cond); } void Await(Condition cond) { WaitLoop(cond); } bool ReaderLockWhenWithTimeout(Condition cond, int millis) { Lock(); return WaitLoopWithTimeout(cond, millis); } bool LockWhenWithTimeout(Condition cond, int millis) { Lock(); return WaitLoopWithTimeout(cond, millis); } bool AwaitWithTimeout(Condition cond, int millis) { return WaitLoopWithTimeout(cond, millis); } private: void WaitLoop(Condition cond) { signal_at_unlock_ = true; while(cond.Eval() == false) { pthread_cond_wait(&cv_, &mu_); } ANNOTATE_HAPPENS_AFTER(this); } bool WaitLoopWithTimeout(Condition cond, int millis) { struct timeval now; struct timespec timeout; int retcode = 0; gettimeofday(&now, NULL); timeval2timespec(&now, &timeout, millis); signal_at_unlock_ = true; while (cond.Eval() == false && retcode == 0) { retcode = pthread_cond_timedwait(&cv_, &mu_, &timeout); } if(retcode == 0) { ANNOTATE_HAPPENS_AFTER(this); } return cond.Eval(); } pthread_mutex_t mu_; // Must be the first member. pthread_cond_t cv_; bool signal_at_unlock_; // Set to true if Wait was called. }; /// Wrapper for pthread_cond_t. class CondVar { public: CondVar() { CHECK(0 == pthread_cond_init(&cv_, NULL)); } ~CondVar() { CHECK(0 == pthread_cond_destroy(&cv_)); } void Wait(Mutex *mu) { CHECK(0 == pthread_cond_wait(&cv_, &mu->mu_)); } bool WaitWithTimeout(Mutex *mu, int millis) { struct timeval now; struct timespec timeout; gettimeofday(&now, NULL); timeval2timespec(&now, &timeout, millis); return 0 != pthread_cond_timedwait(&cv_, &mu->mu_, &timeout); } void Signal() { CHECK(0 == pthread_cond_signal(&cv_)); } void SignalAll() { CHECK(0 == pthread_cond_broadcast(&cv_)); } private: pthread_cond_t cv_; }; // pthreads do not allow to use condvar with rwlock so we can't make // ReaderLock method of Mutex to be the real rw-lock. // So, we need a special lock class to test reader locks. #define NEEDS_SEPERATE_RW_LOCK class RWLock { public: RWLock() { CHECK(0 == pthread_rwlock_init(&mu_, NULL)); } ~RWLock() { CHECK(0 == pthread_rwlock_destroy(&mu_)); } void Lock() { CHECK(0 == pthread_rwlock_wrlock(&mu_)); } void ReaderLock() { CHECK(0 == pthread_rwlock_rdlock(&mu_)); } void Unlock() { CHECK(0 == pthread_rwlock_unlock(&mu_)); } void ReaderUnlock() { CHECK(0 == pthread_rwlock_unlock(&mu_)); } bool TryLock() { int res = pthread_rwlock_trywrlock(&mu_); if (res != 0) { CHECK(EBUSY == res); } return (res == 0); } bool ReaderTryLock() { int res = pthread_rwlock_tryrdlock(&mu_); if (res != 0) { CHECK(EBUSY == res); } return (res == 0); } private: pthread_rwlock_t mu_; }; class ReaderLockScoped { // Scoped RWLock Locker/Unlocker public: ReaderLockScoped(RWLock *mu) : mu_(mu) { mu_->ReaderLock(); } ~ReaderLockScoped() { mu_->ReaderUnlock(); } private: RWLock *mu_; }; class WriterLockScoped { // Scoped RWLock Locker/Unlocker public: WriterLockScoped(RWLock *mu) : mu_(mu) { mu_->Lock(); } ~WriterLockScoped() { mu_->Unlock(); } private: RWLock *mu_; }; #if !defined(__APPLE__) && !defined(ANDROID) class SpinLock { public: SpinLock() { CHECK(0 == pthread_spin_init(&mu_, 0)); } ~SpinLock() { CHECK(0 == pthread_spin_destroy(&mu_)); } void Lock() { CHECK(0 == pthread_spin_lock(&mu_)); } void Unlock() { CHECK(0 == pthread_spin_unlock(&mu_)); } private: pthread_spinlock_t mu_; }; #elif defined(__APPLE__) class SpinLock { public: // Mac OS X version. SpinLock() : mu_(OS_SPINLOCK_INIT) { ANNOTATE_RWLOCK_CREATE((void*)&mu_); } ~SpinLock() { ANNOTATE_RWLOCK_DESTROY((void*)&mu_); } void Lock() { OSSpinLockLock(&mu_); ANNOTATE_RWLOCK_ACQUIRED((void*)&mu_, 1); } void Unlock() { ANNOTATE_RWLOCK_RELEASED((void*)&mu_, 1); OSSpinLockUnlock(&mu_); } private: OSSpinLock mu_; }; #endif // __APPLE__ /// Wrapper for pthread_create()/pthread_join(). class MyThread { public: typedef void *(*worker_t)(void*); MyThread(worker_t worker, void *arg = NULL, const char *name = NULL) :w_(worker), arg_(arg), name_(name) {} MyThread(void (*worker)(void), void *arg = NULL, const char *name = NULL) :w_(reinterpret_cast<worker_t>(worker)), arg_(arg), name_(name) {} MyThread(void (*worker)(void *), void *arg = NULL, const char *name = NULL) :w_(reinterpret_cast<worker_t>(worker)), arg_(arg), name_(name) {} ~MyThread(){ w_ = NULL; arg_ = NULL;} void Start() { CHECK(0 == pthread_create(&t_, NULL, (worker_t)ThreadBody, this));} void Join() { CHECK(0 == pthread_join(t_, NULL));} pthread_t tid() const { return t_; } private: static void ThreadBody(MyThread *my_thread) { if (my_thread->name_) { ANNOTATE_THREAD_NAME(my_thread->name_); } my_thread->w_(my_thread->arg_); } pthread_t t_; worker_t w_; void *arg_; const char *name_; }; #ifndef NO_BARRIER /// Wrapper for pthread_barrier_t. class Barrier{ public: explicit Barrier(int n_threads) {CHECK(0 == pthread_barrier_init(&b_, 0, n_threads));} ~Barrier() {CHECK(0 == pthread_barrier_destroy(&b_));} void Block() { // helgrind 3.3.0 does not have an interceptor for barrier. // but our current local version does. // ANNOTATE_CONDVAR_SIGNAL(this); pthread_barrier_wait(&b_); // ANNOTATE_CONDVAR_WAIT(this, this); } private: pthread_barrier_t b_; }; #endif // NO_BARRIER #endif // THREAD_WRAPPERS_PTHREADS_H_