aboutsummaryrefslogtreecommitdiff
path: root/test/gtest-port_test.cc
diff options
context:
space:
mode:
Diffstat (limited to 'test/gtest-port_test.cc')
-rw-r--r--test/gtest-port_test.cc142
1 files changed, 106 insertions, 36 deletions
diff --git a/test/gtest-port_test.cc b/test/gtest-port_test.cc
index 43f1f20..370c952 100644
--- a/test/gtest-port_test.cc
+++ b/test/gtest-port_test.cc
@@ -1062,11 +1062,13 @@ class AtomicCounterWithMutex {
MutexLock lock(mutex_);
int temp = value_;
{
- // Locking a mutex puts up a memory barrier, preventing reads and
- // writes to value_ rearranged when observed from other threads.
- //
- // We cannot use Mutex and MutexLock here or rely on their memory
- // barrier functionality as we are testing them here.
+ // We need to put up a memory barrier to prevent reads and writes to
+ // value_ rearranged with the call to SleepMilliseconds when observed
+ // from other threads.
+#if GTEST_HAS_PTHREAD
+ // On POSIX, locking a mutex puts up a memory barrier. 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;
GTEST_CHECK_POSIX_SUCCESS_(
pthread_mutex_init(&memory_barrier_mutex, NULL));
@@ -1076,6 +1078,15 @@ class AtomicCounterWithMutex {
GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_unlock(&memory_barrier_mutex));
GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_destroy(&memory_barrier_mutex));
+#elif GTEST_OS_WINDOWS
+ // On Windows, performing an interlocked access puts up a memory barrier.
+ volatile LONG dummy = 0;
+ ::InterlockedIncrement(&dummy);
+ SleepMilliseconds(random_.Generate(30));
+ ::InterlockedIncrement(&dummy);
+#else
+# error "Memory barrier not implemented on this platform."
+#endif // GTEST_HAS_PTHREAD
}
value_ = temp + 1;
}
@@ -1145,27 +1156,76 @@ TEST(ThreadLocalTest, ParameterizedConstructorSetsDefault) {
EXPECT_STREQ("foo", result.c_str());
}
+// Keeps track of whether of destructors being called on instances of
+// DestructorTracker. On Windows, waits for the destructor call reports.
+class DestructorCall {
+ public:
+ DestructorCall() {
+ invoked_ = false;
+#if GTEST_OS_WINDOWS
+ wait_event_.Reset(::CreateEvent(NULL, TRUE, FALSE, NULL));
+ GTEST_CHECK_(wait_event_.Get() != NULL);
+#endif
+ }
+
+ bool CheckDestroyed() const {
+#if GTEST_OS_WINDOWS
+ if (::WaitForSingleObject(wait_event_.Get(), 1000) != WAIT_OBJECT_0)
+ return false;
+#endif
+ return invoked_;
+ }
+
+ void ReportDestroyed() {
+ invoked_ = true;
+#if GTEST_OS_WINDOWS
+ ::SetEvent(wait_event_.Get());
+#endif
+ }
+
+ static std::vector<DestructorCall*>& List() { return *list_; }
+
+ static void ResetList() {
+ for (size_t i = 0; i < list_->size(); ++i) {
+ delete list_->at(i);
+ }
+ list_->clear();
+ }
+
+ private:
+ bool invoked_;
+#if GTEST_OS_WINDOWS
+ AutoHandle wait_event_;
+#endif
+ static std::vector<DestructorCall*>* const list_;
+
+ GTEST_DISALLOW_COPY_AND_ASSIGN_(DestructorCall);
+};
+
+std::vector<DestructorCall*>* const DestructorCall::list_ =
+ new std::vector<DestructorCall*>;
+
// DestructorTracker keeps track of whether its instances have been
// destroyed.
-static std::vector<bool> g_destroyed;
-
class DestructorTracker {
public:
DestructorTracker() : index_(GetNewIndex()) {}
DestructorTracker(const DestructorTracker& /* rhs */)
: index_(GetNewIndex()) {}
~DestructorTracker() {
- // We never access g_destroyed concurrently, so we don't need to
- // protect the write operation under a mutex.
- g_destroyed[index_] = true;
+ // We never access DestructorCall::List() concurrently, so we don't need
+ // to protect this acccess with a mutex.
+ DestructorCall::List()[index_]->ReportDestroyed();
}
private:
static int GetNewIndex() {
- g_destroyed.push_back(false);
- return g_destroyed.size() - 1;
+ DestructorCall::List().push_back(new DestructorCall);
+ return DestructorCall::List().size() - 1;
}
const int index_;
+
+ GTEST_DISALLOW_ASSIGN_(DestructorTracker);
};
typedef ThreadLocal<DestructorTracker>* ThreadParam;
@@ -1177,63 +1237,63 @@ void CallThreadLocalGet(ThreadParam thread_local_param) {
// Tests that when a ThreadLocal object dies in a thread, it destroys
// the managed object for that thread.
TEST(ThreadLocalTest, DestroysManagedObjectForOwnThreadWhenDying) {
- g_destroyed.clear();
+ DestructorCall::ResetList();
{
// The next line default constructs a DestructorTracker object as
// the default value of objects managed by thread_local_tracker.
ThreadLocal<DestructorTracker> thread_local_tracker;
- ASSERT_EQ(1U, g_destroyed.size());
- ASSERT_FALSE(g_destroyed[0]);
+ ASSERT_EQ(1U, DestructorCall::List().size());
+ ASSERT_FALSE(DestructorCall::List()[0]->CheckDestroyed());
// This creates another DestructorTracker object for the main thread.
thread_local_tracker.get();
- ASSERT_EQ(2U, g_destroyed.size());
- ASSERT_FALSE(g_destroyed[0]);
- ASSERT_FALSE(g_destroyed[1]);
+ ASSERT_EQ(2U, DestructorCall::List().size());
+ ASSERT_FALSE(DestructorCall::List()[0]->CheckDestroyed());
+ ASSERT_FALSE(DestructorCall::List()[1]->CheckDestroyed());
}
// Now thread_local_tracker has died. It should have destroyed both the
// default value shared by all threads and the value for the main
// thread.
- ASSERT_EQ(2U, g_destroyed.size());
- EXPECT_TRUE(g_destroyed[0]);
- EXPECT_TRUE(g_destroyed[1]);
+ ASSERT_EQ(2U, DestructorCall::List().size());
+ EXPECT_TRUE(DestructorCall::List()[0]->CheckDestroyed());
+ EXPECT_TRUE(DestructorCall::List()[1]->CheckDestroyed());
- g_destroyed.clear();
+ DestructorCall::ResetList();
}
// Tests that when a thread exits, the thread-local object for that
// thread is destroyed.
TEST(ThreadLocalTest, DestroysManagedObjectAtThreadExit) {
- g_destroyed.clear();
+ DestructorCall::ResetList();
{
// The next line default constructs a DestructorTracker object as
// the default value of objects managed by thread_local_tracker.
ThreadLocal<DestructorTracker> thread_local_tracker;
- ASSERT_EQ(1U, g_destroyed.size());
- ASSERT_FALSE(g_destroyed[0]);
+ ASSERT_EQ(1U, DestructorCall::List().size());
+ ASSERT_FALSE(DestructorCall::List()[0]->CheckDestroyed());
// This creates another DestructorTracker object in the new thread.
ThreadWithParam<ThreadParam> thread(
&CallThreadLocalGet, &thread_local_tracker, NULL);
thread.Join();
- // Now the new thread has exited. The per-thread object for it
- // should have been destroyed.
- ASSERT_EQ(2U, g_destroyed.size());
- ASSERT_FALSE(g_destroyed[0]);
- ASSERT_TRUE(g_destroyed[1]);
+ // The thread has exited, and we should have another DestroyedTracker
+ // instance created for it. But it may not have been destroyed yet.
+ // The instance for the main thread should still persist.
+ ASSERT_EQ(2U, DestructorCall::List().size());
+ ASSERT_FALSE(DestructorCall::List()[0]->CheckDestroyed());
}
- // Now thread_local_tracker has died. The default value should have been
- // destroyed too.
- ASSERT_EQ(2U, g_destroyed.size());
- EXPECT_TRUE(g_destroyed[0]);
- EXPECT_TRUE(g_destroyed[1]);
+ // The thread has exited and thread_local_tracker has died. The default
+ // value should have been destroyed too.
+ ASSERT_EQ(2U, DestructorCall::List().size());
+ EXPECT_TRUE(DestructorCall::List()[0]->CheckDestroyed());
+ EXPECT_TRUE(DestructorCall::List()[1]->CheckDestroyed());
- g_destroyed.clear();
+ DestructorCall::ResetList();
}
TEST(ThreadLocalTest, ThreadLocalMutationsAffectOnlyCurrentThread) {
@@ -1249,5 +1309,15 @@ TEST(ThreadLocalTest, ThreadLocalMutationsAffectOnlyCurrentThread) {
#endif // GTEST_IS_THREADSAFE
+#if GTEST_OS_WINDOWS
+TEST(WindowsTypesTest, HANDLEIsVoidStar) {
+ StaticAssertTypeEq<HANDLE, void*>();
+}
+
+TEST(WindowsTypesTest, CRITICAL_SECTIONIs_RTL_CRITICAL_SECTION) {
+ StaticAssertTypeEq<CRITICAL_SECTION, _RTL_CRITICAL_SECTION>();
+}
+#endif // GTEST_OS_WINDOWS
+
} // namespace internal
} // namespace testing