Commit a6340420 by kosak

Implement threading support for gtest on Windows.

Also, stop using localtime(). Instead, use localtime_r() on most systems, localtime_s() on Windows.
parent ffea2d60
......@@ -968,32 +968,6 @@ GTEST_API_ void ParseGoogleTestFlagsOnly(int* argc, wchar_t** argv);
// platform.
GTEST_API_ std::string GetLastErrnoDescription();
# if GTEST_OS_WINDOWS
// Provides leak-safe Windows kernel handle ownership.
class AutoHandle {
public:
AutoHandle() : handle_(INVALID_HANDLE_VALUE) {}
explicit AutoHandle(HANDLE handle) : handle_(handle) {}
~AutoHandle() { Reset(); }
HANDLE Get() const { return handle_; }
void Reset() { Reset(INVALID_HANDLE_VALUE); }
void Reset(HANDLE handle) {
if (handle != handle_) {
if (handle_ != INVALID_HANDLE_VALUE)
::CloseHandle(handle_);
handle_ = handle;
}
}
private:
HANDLE handle_;
GTEST_DISALLOW_COPY_AND_ASSIGN_(AutoHandle);
};
# endif // GTEST_OS_WINDOWS
// Attempts to parse a string into a positive integer pointed to by the
// number parameter. Returns true if that is possible.
// GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we can use
......
......@@ -3219,27 +3219,23 @@ std::string FormatTimeInMillisAsSeconds(TimeInMillis ms) {
// Converts the given epoch time in milliseconds to a date string in the ISO
// 8601 format, without the timezone information.
std::string FormatEpochTimeInMillisAsIso8601(TimeInMillis ms) {
// Using non-reentrant version as localtime_r is not portable.
time_t seconds = static_cast<time_t>(ms / 1000);
struct tm time_struct;
#ifdef _MSC_VER
# pragma warning(push) // Saves the current warning state.
# pragma warning(disable:4996) // Temporarily disables warning 4996
// (function or variable may be unsafe).
const struct tm* const time_struct = localtime(&seconds); // NOLINT
# pragma warning(pop) // Restores the warning state again.
if (localtime_s(&time_struct, &seconds) != 0)
return ""; // Invalid ms value
#else
const struct tm* const time_struct = localtime(&seconds); // NOLINT
#endif
if (time_struct == NULL)
if (localtime_r(&seconds, &time_struct) == NULL)
return ""; // Invalid ms value
#endif
// YYYY-MM-DDThh:mm:ss
return StreamableToString(time_struct->tm_year + 1900) + "-" +
String::FormatIntWidth2(time_struct->tm_mon + 1) + "-" +
String::FormatIntWidth2(time_struct->tm_mday) + "T" +
String::FormatIntWidth2(time_struct->tm_hour) + ":" +
String::FormatIntWidth2(time_struct->tm_min) + ":" +
String::FormatIntWidth2(time_struct->tm_sec);
return StreamableToString(time_struct.tm_year + 1900) + "-" +
String::FormatIntWidth2(time_struct.tm_mon + 1) + "-" +
String::FormatIntWidth2(time_struct.tm_mday) + "T" +
String::FormatIntWidth2(time_struct.tm_hour) + ":" +
String::FormatIntWidth2(time_struct.tm_min) + ":" +
String::FormatIntWidth2(time_struct.tm_sec);
}
// Streams an XML CDATA section, escaping invalid CDATA sequences as needed.
......
......@@ -699,7 +699,10 @@ TEST_F(TestForDeathTest, ExpectDebugDeathDoesNotAbort) {
void AssertDebugDeathHelper(bool* aborted) {
*aborted = true;
ASSERT_DEBUG_DEATH(return, "") << "This is expected to fail.";
GTEST_LOG_(INFO) << "Before ASSERT_DEBUG_DEATH";
ASSERT_DEBUG_DEATH(GTEST_LOG_(INFO) << "In ASSERT_DEBUG_DEATH"; return, "")
<< "This is expected to fail.";
GTEST_LOG_(INFO) << "After ASSERT_DEBUG_DEATH";
*aborted = false;
}
......@@ -712,6 +715,69 @@ TEST_F(TestForDeathTest, AssertDebugDeathAborts) {
EXPECT_TRUE(aborted);
}
TEST_F(TestForDeathTest, AssertDebugDeathAborts2) {
static bool aborted;
aborted = false;
EXPECT_FATAL_FAILURE(AssertDebugDeathHelper(&aborted), "");
EXPECT_TRUE(aborted);
}
TEST_F(TestForDeathTest, AssertDebugDeathAborts3) {
static bool aborted;
aborted = false;
EXPECT_FATAL_FAILURE(AssertDebugDeathHelper(&aborted), "");
EXPECT_TRUE(aborted);
}
TEST_F(TestForDeathTest, AssertDebugDeathAborts4) {
static bool aborted;
aborted = false;
EXPECT_FATAL_FAILURE(AssertDebugDeathHelper(&aborted), "");
EXPECT_TRUE(aborted);
}
TEST_F(TestForDeathTest, AssertDebugDeathAborts5) {
static bool aborted;
aborted = false;
EXPECT_FATAL_FAILURE(AssertDebugDeathHelper(&aborted), "");
EXPECT_TRUE(aborted);
}
TEST_F(TestForDeathTest, AssertDebugDeathAborts6) {
static bool aborted;
aborted = false;
EXPECT_FATAL_FAILURE(AssertDebugDeathHelper(&aborted), "");
EXPECT_TRUE(aborted);
}
TEST_F(TestForDeathTest, AssertDebugDeathAborts7) {
static bool aborted;
aborted = false;
EXPECT_FATAL_FAILURE(AssertDebugDeathHelper(&aborted), "");
EXPECT_TRUE(aborted);
}
TEST_F(TestForDeathTest, AssertDebugDeathAborts8) {
static bool aborted;
aborted = false;
EXPECT_FATAL_FAILURE(AssertDebugDeathHelper(&aborted), "");
EXPECT_TRUE(aborted);
}
TEST_F(TestForDeathTest, AssertDebugDeathAborts9) {
static bool aborted;
aborted = false;
EXPECT_FATAL_FAILURE(AssertDebugDeathHelper(&aborted), "");
EXPECT_TRUE(aborted);
}
TEST_F(TestForDeathTest, AssertDebugDeathAborts10) {
static bool aborted;
aborted = false;
EXPECT_FATAL_FAILURE(AssertDebugDeathHelper(&aborted), "");
EXPECT_TRUE(aborted);
}
# endif // _NDEBUG
// Tests the *_EXIT family of macros, using a variety of predicates.
......
......@@ -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
......@@ -252,8 +252,8 @@ SUPPORTS_STACK_TRACES = False
CAN_GENERATE_GOLDEN_FILE = (SUPPORTS_DEATH_TESTS and
SUPPORTS_TYPED_TESTS and
SUPPORTS_THREADS)
SUPPORTS_THREADS and
not IS_WINDOWS)
class GTestOutputTest(gtest_test_utils.TestCase):
def RemoveUnsupportedTests(self, test_output):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment