summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorzhanyong.wan <zhanyong.wan@861a406c-534a-0410-8894-cb66d6ee9925>2010-02-25 21:40:08 +0000
committerzhanyong.wan <zhanyong.wan@861a406c-534a-0410-8894-cb66d6ee9925>2010-02-25 21:40:08 +0000
commitf33b34537347ac5f16774649f79cd5b5b9128b13 (patch)
tree0d863a245e0b64bc985e6d75e55e1a773e825792
parent93d13a8bbcb70bfd80b0d7ae2bf9aedfc06bf0cc (diff)
downloadgoogletest-f33b34537347ac5f16774649f79cd5b5b9128b13.tar.gz
Simplifies the threading implementation and improves some comments.
git-svn-id: http://googletest.googlecode.com/svn/trunk@378 861a406c-534a-0410-8894-cb66d6ee9925
-rw-r--r--include/gtest/internal/gtest-port.h218
-rw-r--r--src/gtest-port.cc66
-rw-r--r--test/gtest-port_test.cc30
3 files changed, 127 insertions, 187 deletions
diff --git a/include/gtest/internal/gtest-port.h b/include/gtest/internal/gtest-port.h
index 3894124..bdf75e2 100644
--- a/include/gtest/internal/gtest-port.h
+++ b/include/gtest/internal/gtest-port.h
@@ -733,6 +733,16 @@ inline void FlushInfoLog() { fflush(NULL); }
else \
GTEST_LOG_(FATAL) << "Condition " #condition " failed. "
+// An all-mode assert to verify that the given POSIX-style function
+// call returns 0 (indicating success). Known limitation: this
+// doesn't expand to a balanced 'if' statement, so enclose the macro
+// in {} if you need to use it as the only statement in an 'if'
+// branch.
+#define GTEST_CHECK_POSIX_SUCCESS_(posix_call) \
+ if (const int gtest_error = (posix_call)) \
+ GTEST_LOG_(FATAL) << #posix_call << "failed with error " \
+ << gtest_error
+
#if GTEST_HAS_STREAM_REDIRECTION_
// Defines the stderr capturer:
@@ -770,60 +780,81 @@ const ::std::vector<String>& GetArgvs();
// MutexBase and Mutex implement mutex on pthreads-based platforms. They
// are used in conjunction with class MutexLock:
//
-// Mutex mutex;
-// ...
-// MutexLock lock(&mutex); // Acquires the mutex and releases it at the end
-// // of the current scope.
+// Mutex mutex;
+// ...
+// MutexLock lock(&mutex); // Acquires the mutex and releases it at the end
+// // of the current scope.
//
// MutexBase implements behavior for both statically and dynamically
-// allocated mutexes. Do not use the MutexBase type directly. Instead,
-// define a static mutex using the GTEST_DEFINE_STATIC_MUTEX_ macro:
+// allocated mutexes. Do not use MutexBase directly. Instead, write
+// the following to define a static mutex:
//
-// GTEST_DEFINE_STATIC_MUTEX_(g_some_mutex);
+// GTEST_DEFINE_STATIC_MUTEX_(g_some_mutex);
//
-// Such mutex may also be forward-declared:
+// You can forward declare a static mutex like this:
//
-// GTEST_DECLARE_STATIC_MUTEX_(g_some_mutex);
+// GTEST_DECLARE_STATIC_MUTEX_(g_some_mutex);
//
-// Do not use MutexBase for dynamic mutexes either. Use the Mutex class
-// for them.
+// To create a dynamic mutex, just define an object of type Mutex.
class MutexBase {
public:
- void Lock();
- void Unlock();
+ // Acquires this mutex.
+ void Lock() {
+ GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_lock(&mutex_));
+ owner_ = pthread_self();
+ }
+
+ // Releases this mutex.
+ void Unlock() {
+ // We don't protect writing to owner_ here, as it's the caller's
+ // responsibility to ensure that the current thread holds the
+ // mutex when this is called.
+ owner_ = 0;
+ GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_unlock(&mutex_));
+ }
// Does nothing if the current thread holds the mutex. Otherwise, crashes
// with high probability.
- void AssertHeld() const;
+ void AssertHeld() const {
+ GTEST_CHECK_(owner_ == pthread_self())
+ << "The current thread is not holding the mutex @" << this;
+ }
- // We must be able to initialize objects of MutexBase used as static
- // mutexes with initializer lists. This means MutexBase has to be a POD.
- // The class members have to be public.
+ // A static mutex may be used before main() is entered. It may even
+ // be used before the dynamic initialization stage. Therefore we
+ // must be able to initialize a static mutex object at link time.
+ // This means MutexBase has to be a POD and its member variables
+ // have to be public.
public:
- pthread_mutex_t mutex_;
- pthread_t owner_;
+ pthread_mutex_t mutex_; // The underlying pthread mutex.
+ pthread_t owner_; // The thread holding the mutex; 0 means no one holds it.
};
// Forward-declares a static mutex.
#define GTEST_DECLARE_STATIC_MUTEX_(mutex) \
extern ::testing::internal::MutexBase mutex
-// Defines and statically initializes a static mutex.
+// Defines and statically (i.e. at link time) initializes a static mutex.
#define GTEST_DEFINE_STATIC_MUTEX_(mutex) \
::testing::internal::MutexBase mutex = { PTHREAD_MUTEX_INITIALIZER, 0 }
-// The class Mutex supports only mutexes created at runtime. It shares its
-// API with MutexBase otherwise.
+// The Mutex class can only be used for mutexes created at runtime. It
+// shares its API with MutexBase otherwise.
class Mutex : public MutexBase {
public:
- Mutex();
- ~Mutex();
+ Mutex() {
+ GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_init(&mutex_, NULL));
+ owner_ = 0;
+ }
+ ~Mutex() {
+ GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_destroy(&mutex_));
+ }
private:
GTEST_DISALLOW_COPY_AND_ASSIGN_(Mutex);
};
-// We cannot call it MutexLock directly as the ctor declaration would
+// We cannot name this class MutexLock as the ctor declaration would
// conflict with a macro named MutexLock, which is defined on some
// platforms. Hence the typedef trick below.
class GTestMutexLock {
@@ -843,40 +874,34 @@ typedef GTestMutexLock MutexLock;
// Implements thread-local storage on pthreads-based systems.
//
-// // Thread 1
-// ThreadLocal<int> tl(100);
+// // Thread 1
+// ThreadLocal<int> tl(100); // 100 is the default value for each thread.
//
-// // Thread 2
-// tl.set(150);
-// EXPECT_EQ(150, tl.get());
+// // Thread 2
+// tl.set(150); // Changes the value for thread 2 only.
+// EXPECT_EQ(150, tl.get());
//
-// // Thread 1
-// EXPECT_EQ(100, tl.get()); // On Thread 1, tl.get() returns original value.
-// tl.set(200);
-// EXPECT_EQ(200, tl.get());
+// // Thread 1
+// EXPECT_EQ(100, tl.get()); // In thread 1, tl has the original value.
+// tl.set(200);
+// EXPECT_EQ(200, tl.get());
//
-// The default ThreadLocal constructor requires T to have a default
-// constructor. The single param constructor requires a copy contructor
-// from T. A per-thread object managed by a ThreadLocal instance for a
-// thread is guaranteed to exist at least until the earliest of the two
-// events: (a) the thread terminates or (b) the ThreadLocal object
-// managing it is destroyed.
+// The template type argument T must have a public copy constructor.
+// In addition, the default ThreadLocal constructor requires T to have
+// a public default constructor. An object managed by a ThreadLocal
+// instance for a thread is guaranteed to exist at least until the
+// earliest of the two events: (a) the thread terminates or (b) the
+// ThreadLocal object is destroyed.
template <typename T>
class ThreadLocal {
public:
- ThreadLocal()
- : key_(CreateKey()),
- default_(),
- instance_creator_func_(DefaultConstructNewInstance) {}
-
- explicit ThreadLocal(const T& value)
- : key_(CreateKey()),
- default_(value),
- instance_creator_func_(CopyConstructNewInstance) {}
+ ThreadLocal() : key_(CreateKey()),
+ default_() {}
+ explicit ThreadLocal(const T& value) : key_(CreateKey()),
+ default_(value) {}
~ThreadLocal() {
- const int err = pthread_key_delete(key_);
- GTEST_CHECK_(err == 0) << "pthread_key_delete failed with error " << err;
+ GTEST_CHECK_POSIX_SUCCESS_(pthread_key_delete(key_));
}
T* pointer() { return GetOrCreateValue(); }
@@ -887,54 +912,43 @@ class ThreadLocal {
private:
static pthread_key_t CreateKey() {
pthread_key_t key;
- const int err = pthread_key_create(&key, &DeleteData);
- GTEST_CHECK_(err == 0) << "pthread_key_create failed with error " << err;
+ GTEST_CHECK_POSIX_SUCCESS_(pthread_key_create(&key, &DeleteData));
return key;
}
T* GetOrCreateValue() const {
- T* value = static_cast<T*>(pthread_getspecific(key_));
- if (value == NULL) {
- value = (*instance_creator_func_)(default_);
- const int err = pthread_setspecific(key_, value);
- GTEST_CHECK_(err == 0) << "pthread_setspecific failed with error " << err;
- }
- return value;
+ T* const value = static_cast<T*>(pthread_getspecific(key_));
+ if (value != NULL)
+ return value;
+
+ T* const new_value = new T(default_);
+ GTEST_CHECK_POSIX_SUCCESS_(pthread_setspecific(key_, new_value));
+ return new_value;
}
static void DeleteData(void* data) { delete static_cast<T*>(data); }
- static T* DefaultConstructNewInstance(const T&) { return new T(); }
-
- // Copy constructs new instance of T from default_. Will not be
- // instantiated unless this ThreadLocal is constructed by the single
- // parameter constructor.
- static T* CopyConstructNewInstance(const T& t) { return new T(t); }
-
// A key pthreads uses for looking up per-thread values.
const pthread_key_t key_;
- // Contains the value that CopyConstructNewInstance copies from.
- const T default_;
- // Points to either DefaultConstructNewInstance or CopyConstructNewInstance.
- T* (*const instance_creator_func_)(const T& default_);
+ const T default_; // The default value for each thread.
GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadLocal);
};
-// Allows the controller thread pause execution of newly created test
-// threads until signalled. Instances of this class must be created and
-// destroyed in the controller thread.
+// Allows a controller thread to pause execution of newly created
+// threads until signalled. Instances of this class must be created
+// and destroyed in the controller thread.
//
-// This class is supplied only for the purpose of testing Google Test's own
+// This class is supplied only for testing Google Test's own
// constructs. Do not use it in user tests, either directly or indirectly.
class ThreadStartSemaphore {
public:
ThreadStartSemaphore();
~ThreadStartSemaphore();
- // Signals to all test threads created with this semaphore to start. Must
- // be called from the controlling thread.
+ // Signals to all threads created with this semaphore to start. Must
+ // be called from the controller thread.
void Signal();
- // Blocks until the controlling thread signals. Must be called from a test
+ // Blocks until the controller thread signals. Must be called from a test
// thread.
void Wait();
@@ -947,17 +961,17 @@ class ThreadStartSemaphore {
GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadStartSemaphore);
};
-// Helper class for testing Google Test's multithreading constructs.
+// Helper class for testing Google Test's multi-threading constructs.
// Use:
//
-// void ThreadFunc(int param) { /* Do things with param */ }
-// ThreadSemaphore semaphore;
-// ...
-// // The semaphore parameter is optional; you can supply NULL.
-// ThredWithParam<int> thread(&ThreadFunc, 5, &semaphore);
-// sem.Signal(); // Allows the thread to start.
+// void ThreadFunc(int param) { /* Do things with param */ }
+// ThreadStartSemaphore semaphore;
+// ...
+// // The semaphore parameter is optional; you can supply NULL.
+// ThreadWithParam<int> thread(&ThreadFunc, 5, &semaphore);
+// semaphore.Signal(); // Allows the thread to start.
//
-// This class is supplied only for the purpose of testing Google Test's own
+// This class is supplied only for testing Google Test's own
// constructs. Do not use it in user tests, either directly or indirectly.
template <typename T>
class ThreadWithParam {
@@ -969,19 +983,16 @@ class ThreadWithParam {
param_(param),
start_semaphore_(semaphore),
finished_(false) {
- // func_, param_, and start_semaphore_ must be initialized before
- // pthread_create() is called.
- const int err = pthread_create(&thread_, 0, ThreadMainStatic, this);
- GTEST_CHECK_(err == 0) << "pthread_create failed with error: "
- << strerror(err) << "(" << err << ")";
+ // The thread can be created only after all fields except thread_
+ // have been initialized.
+ GTEST_CHECK_POSIX_SUCCESS_(
+ pthread_create(&thread_, 0, ThreadMainStatic, this));
}
~ThreadWithParam() { Join(); }
void Join() {
if (!finished_) {
- const int err = pthread_join(thread_, 0);
- GTEST_CHECK_(err == 0) << "pthread_join failed with error:"
- << strerror(err) << "(" << err << ")";
+ GTEST_CHECK_POSIX_SUCCESS_(pthread_join(thread_, 0));
finished_ = true;
}
}
@@ -992,23 +1003,18 @@ class ThreadWithParam {
start_semaphore_->Wait();
func_(param_);
}
- static void* ThreadMainStatic(void* param) {
- static_cast<ThreadWithParam<T>*>(param)->ThreadMain();
- return NULL; // We are not interested in thread exit code.
+ static void* ThreadMainStatic(void* thread_with_param) {
+ static_cast<ThreadWithParam<T>*>(thread_with_param)->ThreadMain();
+ return NULL; // We are not interested in the thread exit code.
}
- // User supplied thread function.
- const UserThreadFunc func_;
- // User supplied parameter to UserThreadFunc.
- const T param_;
-
- // Native thread object.
- pthread_t thread_;
+ const UserThreadFunc func_; // User-supplied thread function.
+ const T param_; // User-supplied parameter to the thread function.
// When non-NULL, used to block execution until the controller thread
// signals.
ThreadStartSemaphore* const start_semaphore_;
- // true iff UserThreadFunc has not completed yet.
- bool finished_;
+ bool finished_; // Has the thread function finished?
+ pthread_t thread_; // The native thread object.
};
#define GTEST_IS_THREADSAFE 1
diff --git a/src/gtest-port.cc b/src/gtest-port.cc
index 5994fd5..1c4c1bd 100644
--- a/src/gtest-port.cc
+++ b/src/gtest-port.cc
@@ -81,10 +81,8 @@ const int kStdErrFileno = STDERR_FILENO;
// newly created test threads until signalled. Instances of this class must
// be created and destroyed in the controller thread.
ThreadStartSemaphore::ThreadStartSemaphore() : signalled_(false) {
- int err = pthread_mutex_init(&mutex_, NULL);
- GTEST_CHECK_(err == 0) << "pthread_mutex_init failed with error " << err;
- err = pthread_cond_init(&cond_, NULL);
- GTEST_CHECK_(err == 0) << "pthread_cond_init failed with error " << err;
+ GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_init(&mutex_, NULL));
+ GTEST_CHECK_POSIX_SUCCESS_(pthread_cond_init(&cond_, NULL));
pthread_mutex_lock(&mutex_);
}
@@ -92,75 +90,29 @@ ThreadStartSemaphore::~ThreadStartSemaphore() {
// Every ThreadStartSemaphore object must be signalled. It locks
// internal mutex upon creation and Signal unlocks it.
GTEST_CHECK_(signalled_);
-
- int err = pthread_mutex_destroy(&mutex_);
- GTEST_CHECK_(err == 0)
- << "pthread_mutex_destroy failed with error " << err;
- err = pthread_cond_destroy(&cond_);
- GTEST_CHECK_(err == 0)
- << "pthread_cond_destroy failed with error " << err;
+ GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_destroy(&mutex_));
+ GTEST_CHECK_POSIX_SUCCESS_(pthread_cond_destroy(&cond_));
}
// Signals to all test threads to start. Must be called from the
// controlling thread.
void ThreadStartSemaphore::Signal() {
signalled_ = true;
- int err = pthread_cond_signal(&cond_);
- GTEST_CHECK_(err == 0)
- << "pthread_cond_signal failed with error " << err;
- err = pthread_mutex_unlock(&mutex_);
- GTEST_CHECK_(err == 0)
- << "pthread_mutex_unlock failed with error " << err;
+ GTEST_CHECK_POSIX_SUCCESS_(pthread_cond_signal(&cond_));
+ GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_unlock(&mutex_));
}
// Blocks until the controlling thread signals. Should be called from a
// test thread.
void ThreadStartSemaphore::Wait() {
- int err = pthread_mutex_lock(&mutex_);
- GTEST_CHECK_(err == 0) << "pthread_mutex_lock failed with error " << err;
+ GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_lock(&mutex_));
while (!signalled_) {
- err = pthread_cond_wait(&cond_, &mutex_);
- GTEST_CHECK_(err == 0)
- << "pthread_cond_wait failed with error " << err;
+ GTEST_CHECK_POSIX_SUCCESS_(pthread_cond_wait(&cond_, &mutex_));
}
- err = pthread_mutex_unlock(&mutex_);
- GTEST_CHECK_(err == 0)
- << "pthread_mutex_unlock failed with error " << err;
-}
-
-void MutexBase::Lock() {
- const int err = pthread_mutex_lock(&mutex_);
- GTEST_CHECK_(err == 0) << "pthread_mutex_lock failed with error " << err;
- owner_ = pthread_self();
-}
-
-void MutexBase::Unlock() {
- // We don't protect writing to owner_ here, as it's the caller's
- // responsibility to ensure that the current thread holds the mutex when
- // this is called.
- owner_ = 0;
- const int err = pthread_mutex_unlock(&mutex_);
- GTEST_CHECK_(err == 0) << "pthread_mutex_unlock failed with error " << err;
+ GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_unlock(&mutex_));
}
-// Does nothing if the current thread holds the mutex. Otherwise, crashes
-// with high probability.
-void MutexBase::AssertHeld() const {
- GTEST_CHECK_(owner_ == pthread_self())
- << "Current thread is not holding mutex." << this;
-}
-
-Mutex::Mutex() {
- owner_ = 0;
- const int err = pthread_mutex_init(&mutex_, NULL);
- GTEST_CHECK_(err == 0) << "pthread_mutex_init failed with error " << err;
-}
-
-Mutex::~Mutex() {
- const int err = pthread_mutex_destroy(&mutex_);
- GTEST_CHECK_(err == 0) << "pthread_mutex_destroy failed with error " << err;
-}
#endif // GTEST_HAS_PTHREAD
#if GTEST_OS_MAC
diff --git a/test/gtest-port_test.cc b/test/gtest-port_test.cc
index 8594aa9..f7f2621 100644
--- a/test/gtest-port_test.cc
+++ b/test/gtest-port_test.cc
@@ -770,18 +770,6 @@ TEST(ThreadLocalTest, SingleParamConstructorInitializesToParam) {
EXPECT_EQ(&i, t2.get());
}
-class NoCopyConstructor {
- public:
- NoCopyConstructor() {}
- private:
- GTEST_DISALLOW_COPY_AND_ASSIGN_(NoCopyConstructor);
-};
-
-TEST(ThreadLocalTest, ValueCopyConstructorIsNotRequiredForDefaultVersion) {
- ThreadLocal<NoCopyConstructor> bar;
- bar.get();
-}
-
class NoDefaultContructor {
public:
explicit NoDefaultContructor(const char*) {}
@@ -796,9 +784,6 @@ TEST(ThreadLocalTest, ValueDefaultContructorIsNotRequiredForParamVersion) {
TEST(ThreadLocalTest, GetAndPointerReturnSameValue) {
ThreadLocal<String> thread_local;
- // This is why EXPECT_TRUE is used here rather than EXPECT_EQ because
- // we don't care about a particular value of thread_local.pointer() here;
- // we only care about pointer and reference referring to the same lvalue.
EXPECT_EQ(thread_local.pointer(), &(thread_local.get()));
// Verifies the condition still holds after calling set.
@@ -825,7 +810,7 @@ TEST(MutexTestDeathTest, AssertHeldShouldAssertWhenNotLocked) {
{ MutexLock lock(&m); }
m.AssertHeld();
},
- "Current thread is not holding mutex..+");
+ "The current thread is not holding the mutex @.+");
}
void SleepMilliseconds(int time) {
@@ -847,16 +832,13 @@ class AtomicCounterWithMutex {
// We cannot use Mutex and MutexLock here or rely on their memory
// barrier functionality as we are testing them here.
pthread_mutex_t memory_barrier_mutex;
- int err = pthread_mutex_init(&memory_barrier_mutex, NULL);
- GTEST_CHECK_(err == 0) << "pthread_mutex_init failed with error " << err;
- err = pthread_mutex_lock(&memory_barrier_mutex);
- GTEST_CHECK_(err == 0) << "pthread_mutex_lock failed with error " << err;
+ GTEST_CHECK_POSIX_SUCCESS_(
+ pthread_mutex_init(&memory_barrier_mutex, NULL));
+ GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_lock(&memory_barrier_mutex));
SleepMilliseconds(random_.Generate(30));
- err = pthread_mutex_unlock(&memory_barrier_mutex);
- GTEST_CHECK_(err == 0)
- << "pthread_mutex_unlock failed with error " << err;
+ GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_unlock(&memory_barrier_mutex));
}
value_ = temp + 1;
}
@@ -937,7 +919,7 @@ int CountedDestructor::counter_ = 0;
template <typename T>
void CallThreadLocalGet(ThreadLocal<T>* threadLocal) {
- threadLocal->get();
+ threadLocal->get();
}
TEST(ThreadLocalTest, DestroysManagedObjectsNoLaterThanSelf) {