Implement threading support for gtest on Windows.
Also, stop using localtime(). Instead, use localtime_r() on most systems, localtime_s() on Windows.
This commit is contained in:
parent
ffea2d6040
commit
a6340420b9
|
@ -379,16 +379,23 @@
|
||||||
// Brings in definitions for functions used in the testing::internal::posix
|
// Brings in definitions for functions used in the testing::internal::posix
|
||||||
// namespace (read, write, close, chdir, isatty, stat). We do not currently
|
// namespace (read, write, close, chdir, isatty, stat). We do not currently
|
||||||
// use them on Windows Mobile.
|
// use them on Windows Mobile.
|
||||||
#if !GTEST_OS_WINDOWS
|
#if GTEST_OS_WINDOWS
|
||||||
|
# if !GTEST_OS_WINDOWS_MOBILE
|
||||||
|
# include <direct.h>
|
||||||
|
# include <io.h>
|
||||||
|
# endif
|
||||||
|
// In order to avoid having to include <windows.h>, use forward declaration
|
||||||
|
// assuming CRITICAL_SECTION is a typedef of _RTL_CRITICAL_SECTION.
|
||||||
|
// This assumption is verified by
|
||||||
|
// WindowsTypesTest.CRITICAL_SECTIONIs_RTL_CRITICAL_SECTION.
|
||||||
|
struct _RTL_CRITICAL_SECTION;
|
||||||
|
#else
|
||||||
// This assumes that non-Windows OSes provide unistd.h. For OSes where this
|
// This assumes that non-Windows OSes provide unistd.h. For OSes where this
|
||||||
// is not the case, we need to include headers that provide the functions
|
// is not the case, we need to include headers that provide the functions
|
||||||
// mentioned above.
|
// mentioned above.
|
||||||
# include <unistd.h>
|
# include <unistd.h>
|
||||||
# include <strings.h>
|
# include <strings.h>
|
||||||
#elif !GTEST_OS_WINDOWS_MOBILE
|
#endif // GTEST_OS_WINDOWS
|
||||||
# include <direct.h>
|
|
||||||
# include <io.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if GTEST_OS_LINUX_ANDROID
|
#if GTEST_OS_LINUX_ANDROID
|
||||||
// Used to define __ANDROID_API__ matching the target NDK API level.
|
// Used to define __ANDROID_API__ matching the target NDK API level.
|
||||||
|
@ -871,6 +878,9 @@ using ::std::tuple_size;
|
||||||
# define GTEST_HAS_SEH 0
|
# define GTEST_HAS_SEH 0
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
|
#define GTEST_IS_THREADSAFE \
|
||||||
|
(GTEST_OS_WINDOWS || GTEST_HAS_PTHREAD)
|
||||||
|
|
||||||
#endif // GTEST_HAS_SEH
|
#endif // GTEST_HAS_SEH
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
|
@ -1340,12 +1350,11 @@ extern ::std::vector<testing::internal::string> g_argvs;
|
||||||
#endif // GTEST_HAS_DEATH_TEST
|
#endif // GTEST_HAS_DEATH_TEST
|
||||||
|
|
||||||
// Defines synchronization primitives.
|
// Defines synchronization primitives.
|
||||||
|
#if GTEST_IS_THREADSAFE
|
||||||
# if GTEST_HAS_PTHREAD
|
# if GTEST_HAS_PTHREAD
|
||||||
|
// Sleeps for (roughly) n milliseconds. This function is only for testing
|
||||||
// Sleeps for (roughly) n milli-seconds. This function is only for
|
// Google Test's own constructs. Don't use it in user tests, either
|
||||||
// testing Google Test's own constructs. Don't use it in user tests,
|
// directly or indirectly.
|
||||||
// either directly or indirectly.
|
|
||||||
inline void SleepMilliseconds(int n) {
|
inline void SleepMilliseconds(int n) {
|
||||||
const timespec time = {
|
const timespec time = {
|
||||||
0, // 0 seconds.
|
0, // 0 seconds.
|
||||||
|
@ -1353,7 +1362,10 @@ inline void SleepMilliseconds(int n) {
|
||||||
};
|
};
|
||||||
nanosleep(&time, NULL);
|
nanosleep(&time, NULL);
|
||||||
}
|
}
|
||||||
|
# endif // GTEST_HAS_PTHREAD
|
||||||
|
|
||||||
|
# if 0 // OS detection
|
||||||
|
# elif GTEST_HAS_PTHREAD
|
||||||
// Allows a controller thread to pause execution of newly created
|
// Allows a controller thread to pause execution of newly created
|
||||||
// threads until notified. Instances of this class must be created
|
// threads until notified. Instances of this class must be created
|
||||||
// and destroyed in the controller thread.
|
// and destroyed in the controller thread.
|
||||||
|
@ -1397,6 +1409,62 @@ class Notification {
|
||||||
GTEST_DISALLOW_COPY_AND_ASSIGN_(Notification);
|
GTEST_DISALLOW_COPY_AND_ASSIGN_(Notification);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# elif GTEST_OS_WINDOWS
|
||||||
|
|
||||||
|
GTEST_API_ void SleepMilliseconds(int n);
|
||||||
|
|
||||||
|
// Provides leak-safe Windows kernel handle ownership.
|
||||||
|
// Used in death tests and in threading support.
|
||||||
|
class GTEST_API_ AutoHandle {
|
||||||
|
public:
|
||||||
|
// Assume that Win32 HANDLE type is equivalent to void*. Doing so allows us to
|
||||||
|
// avoid including <windows.h> in this header file. Including <windows.h> is
|
||||||
|
// undesirable because it defines a lot of symbols and macros that tend to
|
||||||
|
// conflict with client code. This assumption is verified by
|
||||||
|
// WindowsTypesTest.HANDLEIsVoidStar.
|
||||||
|
typedef void* Handle;
|
||||||
|
AutoHandle();
|
||||||
|
explicit AutoHandle(Handle handle);
|
||||||
|
|
||||||
|
~AutoHandle();
|
||||||
|
|
||||||
|
Handle Get() const;
|
||||||
|
void Reset();
|
||||||
|
void Reset(Handle handle);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Returns true iff the handle is a valid handle object that can be closed.
|
||||||
|
bool IsCloseable() const;
|
||||||
|
|
||||||
|
Handle handle_;
|
||||||
|
|
||||||
|
GTEST_DISALLOW_COPY_AND_ASSIGN_(AutoHandle);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Allows a controller thread to pause execution of newly created
|
||||||
|
// threads until notified. Instances of this class must be created
|
||||||
|
// and destroyed in the controller thread.
|
||||||
|
//
|
||||||
|
// This class is only for testing Google Test's own constructs. Do not
|
||||||
|
// use it in user tests, either directly or indirectly.
|
||||||
|
class GTEST_API_ Notification {
|
||||||
|
public:
|
||||||
|
Notification();
|
||||||
|
void Notify();
|
||||||
|
void WaitForNotification();
|
||||||
|
|
||||||
|
private:
|
||||||
|
AutoHandle event_;
|
||||||
|
|
||||||
|
GTEST_DISALLOW_COPY_AND_ASSIGN_(Notification);
|
||||||
|
};
|
||||||
|
# endif // OS detection
|
||||||
|
|
||||||
|
// On MinGW, we can have both GTEST_OS_WINDOWS and GTEST_HAS_PTHREAD
|
||||||
|
// defined, but we don't want to use MinGW's pthreads implementation, which
|
||||||
|
// has conformance problems with some versions of the POSIX standard.
|
||||||
|
# if GTEST_HAS_PTHREAD && !GTEST_OS_WINDOWS_MINGW
|
||||||
|
|
||||||
// As a C-function, ThreadFuncWithCLinkage cannot be templated itself.
|
// As a C-function, ThreadFuncWithCLinkage cannot be templated itself.
|
||||||
// Consequently, it cannot select a correct instantiation of ThreadWithParam
|
// Consequently, it cannot select a correct instantiation of ThreadWithParam
|
||||||
// in order to call its Run(). Introducing ThreadWithParamBase as a
|
// in order to call its Run(). Introducing ThreadWithParamBase as a
|
||||||
|
@ -1434,10 +1502,9 @@ extern "C" inline void* ThreadFuncWithCLinkage(void* thread) {
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class ThreadWithParam : public ThreadWithParamBase {
|
class ThreadWithParam : public ThreadWithParamBase {
|
||||||
public:
|
public:
|
||||||
typedef void (*UserThreadFunc)(T);
|
typedef void UserThreadFunc(T);
|
||||||
|
|
||||||
ThreadWithParam(
|
ThreadWithParam(UserThreadFunc* func, T param, Notification* thread_can_start)
|
||||||
UserThreadFunc func, T param, Notification* thread_can_start)
|
|
||||||
: func_(func),
|
: func_(func),
|
||||||
param_(param),
|
param_(param),
|
||||||
thread_can_start_(thread_can_start),
|
thread_can_start_(thread_can_start),
|
||||||
|
@ -1464,7 +1531,7 @@ class ThreadWithParam : public ThreadWithParamBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const UserThreadFunc func_; // User-supplied thread function.
|
UserThreadFunc* const func_; // User-supplied thread function.
|
||||||
const T param_; // User-supplied parameter to the thread function.
|
const T param_; // User-supplied parameter to the thread function.
|
||||||
// When non-NULL, used to block execution until the controller thread
|
// When non-NULL, used to block execution until the controller thread
|
||||||
// notifies.
|
// notifies.
|
||||||
|
@ -1474,26 +1541,255 @@ class ThreadWithParam : public ThreadWithParamBase {
|
||||||
|
|
||||||
GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadWithParam);
|
GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadWithParam);
|
||||||
};
|
};
|
||||||
|
# endif // GTEST_HAS_PTHREAD && !GTEST_OS_WINDOWS_MINGW
|
||||||
|
|
||||||
// MutexBase and Mutex implement mutex on pthreads-based platforms. They
|
# if 0 // OS detection
|
||||||
// are used in conjunction with class MutexLock:
|
# elif GTEST_OS_WINDOWS
|
||||||
|
|
||||||
|
// Mutex implements mutex on Windows platforms. It is used in conjunction
|
||||||
|
// with class MutexLock:
|
||||||
//
|
//
|
||||||
// Mutex mutex;
|
// Mutex mutex;
|
||||||
// ...
|
// ...
|
||||||
// MutexLock lock(&mutex); // Acquires the mutex and releases it at the end
|
// MutexLock lock(&mutex); // Acquires the mutex and releases it at the
|
||||||
// // of the current scope.
|
// // end of the current scope.
|
||||||
//
|
|
||||||
// MutexBase implements behavior for both statically and dynamically
|
|
||||||
// allocated mutexes. Do not use MutexBase directly. Instead, write
|
|
||||||
// the following to define a static mutex:
|
|
||||||
//
|
//
|
||||||
|
// A static Mutex *must* be defined or declared using one of the following
|
||||||
|
// macros:
|
||||||
// GTEST_DEFINE_STATIC_MUTEX_(g_some_mutex);
|
// GTEST_DEFINE_STATIC_MUTEX_(g_some_mutex);
|
||||||
//
|
|
||||||
// You can forward declare a static mutex like this:
|
|
||||||
//
|
|
||||||
// GTEST_DECLARE_STATIC_MUTEX_(g_some_mutex);
|
// GTEST_DECLARE_STATIC_MUTEX_(g_some_mutex);
|
||||||
//
|
//
|
||||||
// To create a dynamic mutex, just define an object of type Mutex.
|
// (A non-static Mutex is defined/declared in the usual way).
|
||||||
|
class GTEST_API_ Mutex {
|
||||||
|
public:
|
||||||
|
enum MutexType { kStatic = 0, kDynamic = 1 };
|
||||||
|
// We rely on kStaticMutex being 0 as it is to what the linker initializes
|
||||||
|
// type_ in static mutexes. critical_section_ will be initialized lazily
|
||||||
|
// in ThreadSafeLazyInit().
|
||||||
|
enum StaticConstructorSelector { kStaticMutex = 0 };
|
||||||
|
|
||||||
|
// This constructor intentionally does nothing. It relies on type_ being
|
||||||
|
// statically initialized to 0 (effectively setting it to kStatic) and on
|
||||||
|
// ThreadSafeLazyInit() to lazily initialize the rest of the members.
|
||||||
|
explicit Mutex(StaticConstructorSelector /*dummy*/) {}
|
||||||
|
|
||||||
|
Mutex();
|
||||||
|
~Mutex();
|
||||||
|
|
||||||
|
void Lock();
|
||||||
|
|
||||||
|
void Unlock();
|
||||||
|
|
||||||
|
// Does nothing if the current thread holds the mutex. Otherwise, crashes
|
||||||
|
// with high probability.
|
||||||
|
void AssertHeld();
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Initializes owner_thread_id_ and critical_section_ in static mutexes.
|
||||||
|
void ThreadSafeLazyInit();
|
||||||
|
|
||||||
|
// Per http://blogs.msdn.com/b/oldnewthing/archive/2004/02/23/78395.aspx,
|
||||||
|
// we assume that 0 is an invalid value for thread IDs.
|
||||||
|
unsigned int owner_thread_id_;
|
||||||
|
|
||||||
|
// For static mutexes, we rely on these members being initialized to zeros
|
||||||
|
// by the linker.
|
||||||
|
MutexType type_;
|
||||||
|
long critical_section_init_phase_; // NOLINT
|
||||||
|
_RTL_CRITICAL_SECTION* critical_section_;
|
||||||
|
|
||||||
|
GTEST_DISALLOW_COPY_AND_ASSIGN_(Mutex);
|
||||||
|
};
|
||||||
|
|
||||||
|
# define GTEST_DECLARE_STATIC_MUTEX_(mutex) \
|
||||||
|
extern ::testing::internal::Mutex mutex
|
||||||
|
|
||||||
|
# define GTEST_DEFINE_STATIC_MUTEX_(mutex) \
|
||||||
|
::testing::internal::Mutex mutex(::testing::internal::Mutex::kStaticMutex)
|
||||||
|
|
||||||
|
// We cannot name this class MutexLock because the ctor declaration would
|
||||||
|
// conflict with a macro named MutexLock, which is defined on some
|
||||||
|
// platforms. That macro is used as a defensive measure to prevent against
|
||||||
|
// inadvertent misuses of MutexLock like "MutexLock(&mu)" rather than
|
||||||
|
// "MutexLock l(&mu)". Hence the typedef trick below.
|
||||||
|
class GTestMutexLock {
|
||||||
|
public:
|
||||||
|
explicit GTestMutexLock(Mutex* mutex)
|
||||||
|
: mutex_(mutex) { mutex_->Lock(); }
|
||||||
|
|
||||||
|
~GTestMutexLock() { mutex_->Unlock(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Mutex* const mutex_;
|
||||||
|
|
||||||
|
GTEST_DISALLOW_COPY_AND_ASSIGN_(GTestMutexLock);
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef GTestMutexLock MutexLock;
|
||||||
|
|
||||||
|
// Base class for ValueHolder<T>. Allows a caller to hold and delete a value
|
||||||
|
// without knowing its type.
|
||||||
|
class ThreadLocalValueHolderBase {
|
||||||
|
public:
|
||||||
|
virtual ~ThreadLocalValueHolderBase() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Provides a way for a thread to send notifications to a ThreadLocal
|
||||||
|
// regardless of its parameter type.
|
||||||
|
class ThreadLocalBase {
|
||||||
|
public:
|
||||||
|
// Creates a new ValueHolder<T> object holding a default value passed to
|
||||||
|
// this ThreadLocal<T>'s constructor and returns it. It is the caller's
|
||||||
|
// responsibility not to call this when the ThreadLocal<T> instance already
|
||||||
|
// has a value on the current thread.
|
||||||
|
virtual ThreadLocalValueHolderBase* NewValueForCurrentThread() const = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
ThreadLocalBase() {}
|
||||||
|
virtual ~ThreadLocalBase() {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadLocalBase);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Maps a thread to a set of ThreadLocals that have values instantiated on that
|
||||||
|
// thread and notifies them when the thread exits. A ThreadLocal instance is
|
||||||
|
// expected to persist until all threads it has values on have terminated.
|
||||||
|
class GTEST_API_ ThreadLocalRegistry {
|
||||||
|
public:
|
||||||
|
// Registers thread_local_instance as having value on the current thread.
|
||||||
|
// Returns a value that can be used to identify the thread from other threads.
|
||||||
|
static ThreadLocalValueHolderBase* GetValueOnCurrentThread(
|
||||||
|
const ThreadLocalBase* thread_local_instance);
|
||||||
|
|
||||||
|
// Invoked when a ThreadLocal instance is destroyed.
|
||||||
|
static void OnThreadLocalDestroyed(
|
||||||
|
const ThreadLocalBase* thread_local_instance);
|
||||||
|
};
|
||||||
|
|
||||||
|
class GTEST_API_ ThreadWithParamBase {
|
||||||
|
public:
|
||||||
|
void Join();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
class Runnable {
|
||||||
|
public:
|
||||||
|
virtual ~Runnable() {}
|
||||||
|
virtual void Run() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
ThreadWithParamBase(Runnable *runnable, Notification* thread_can_start);
|
||||||
|
virtual ~ThreadWithParamBase();
|
||||||
|
|
||||||
|
private:
|
||||||
|
AutoHandle thread_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper class for testing Google Test's multi-threading constructs.
|
||||||
|
template <typename T>
|
||||||
|
class ThreadWithParam : public ThreadWithParamBase {
|
||||||
|
public:
|
||||||
|
typedef void UserThreadFunc(T);
|
||||||
|
|
||||||
|
ThreadWithParam(UserThreadFunc* func, T param, Notification* thread_can_start)
|
||||||
|
: ThreadWithParamBase(new RunnableImpl(func, param), thread_can_start) {
|
||||||
|
}
|
||||||
|
virtual ~ThreadWithParam() {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
class RunnableImpl : public Runnable {
|
||||||
|
public:
|
||||||
|
RunnableImpl(UserThreadFunc* func, T param)
|
||||||
|
: func_(func),
|
||||||
|
param_(param) {
|
||||||
|
}
|
||||||
|
virtual ~RunnableImpl() {}
|
||||||
|
virtual void Run() {
|
||||||
|
func_(param_);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
UserThreadFunc* const func_;
|
||||||
|
const T param_;
|
||||||
|
|
||||||
|
GTEST_DISALLOW_COPY_AND_ASSIGN_(RunnableImpl);
|
||||||
|
};
|
||||||
|
|
||||||
|
GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadWithParam);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Implements thread-local storage on Windows systems.
|
||||||
|
//
|
||||||
|
// // Thread 1
|
||||||
|
// ThreadLocal<int> tl(100); // 100 is the default value for each thread.
|
||||||
|
//
|
||||||
|
// // Thread 2
|
||||||
|
// tl.set(150); // Changes the value for thread 2 only.
|
||||||
|
// EXPECT_EQ(150, 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 template type argument T must have a public copy constructor.
|
||||||
|
// In addition, the default ThreadLocal constructor requires T to have
|
||||||
|
// a public default constructor.
|
||||||
|
//
|
||||||
|
// The users of a TheadLocal instance have to make sure that all but one
|
||||||
|
// threads (including the main one) using that instance have exited before
|
||||||
|
// destroying it. Otherwise, the per-thread objects managed for them by the
|
||||||
|
// ThreadLocal instance are not guaranteed to be destroyed on all platforms.
|
||||||
|
//
|
||||||
|
// Google Test only uses global ThreadLocal objects. That means they
|
||||||
|
// will die after main() has returned. Therefore, no per-thread
|
||||||
|
// object managed by Google Test will be leaked as long as all threads
|
||||||
|
// using Google Test have exited when main() returns.
|
||||||
|
template <typename T>
|
||||||
|
class ThreadLocal : public ThreadLocalBase {
|
||||||
|
public:
|
||||||
|
ThreadLocal() : default_() {}
|
||||||
|
explicit ThreadLocal(const T& value) : default_(value) {}
|
||||||
|
|
||||||
|
~ThreadLocal() { ThreadLocalRegistry::OnThreadLocalDestroyed(this); }
|
||||||
|
|
||||||
|
T* pointer() { return GetOrCreateValue(); }
|
||||||
|
const T* pointer() const { return GetOrCreateValue(); }
|
||||||
|
const T& get() const { return *pointer(); }
|
||||||
|
void set(const T& value) { *pointer() = value; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Holds a value of T. Can be deleted via its base class without the caller
|
||||||
|
// knowing the type of T.
|
||||||
|
class ValueHolder : public ThreadLocalValueHolderBase {
|
||||||
|
public:
|
||||||
|
explicit ValueHolder(const T& value) : value_(value) {}
|
||||||
|
|
||||||
|
T* pointer() { return &value_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
T value_;
|
||||||
|
GTEST_DISALLOW_COPY_AND_ASSIGN_(ValueHolder);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
T* GetOrCreateValue() const {
|
||||||
|
return static_cast<ValueHolder*>(
|
||||||
|
ThreadLocalRegistry::GetValueOnCurrentThread(this))->pointer();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ThreadLocalValueHolderBase* NewValueForCurrentThread() const {
|
||||||
|
return new ValueHolder(default_);
|
||||||
|
}
|
||||||
|
|
||||||
|
const T default_; // The default value for each thread.
|
||||||
|
|
||||||
|
GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadLocal);
|
||||||
|
};
|
||||||
|
|
||||||
|
# elif GTEST_HAS_PTHREAD
|
||||||
|
|
||||||
|
// MutexBase and Mutex implement mutex on pthreads-based platforms.
|
||||||
class MutexBase {
|
class MutexBase {
|
||||||
public:
|
public:
|
||||||
// Acquires this mutex.
|
// Acquires this mutex.
|
||||||
|
@ -1566,9 +1862,11 @@ class Mutex : public MutexBase {
|
||||||
GTEST_DISALLOW_COPY_AND_ASSIGN_(Mutex);
|
GTEST_DISALLOW_COPY_AND_ASSIGN_(Mutex);
|
||||||
};
|
};
|
||||||
|
|
||||||
// We cannot name this class MutexLock as the ctor declaration would
|
// We cannot name this class MutexLock because the ctor declaration would
|
||||||
// conflict with a macro named MutexLock, which is defined on some
|
// conflict with a macro named MutexLock, which is defined on some
|
||||||
// platforms. Hence the typedef trick below.
|
// platforms. That macro is used as a defensive measure to prevent against
|
||||||
|
// inadvertent misuses of MutexLock like "MutexLock(&mu)" rather than
|
||||||
|
// "MutexLock l(&mu)". Hence the typedef trick below.
|
||||||
class GTestMutexLock {
|
class GTestMutexLock {
|
||||||
public:
|
public:
|
||||||
explicit GTestMutexLock(MutexBase* mutex)
|
explicit GTestMutexLock(MutexBase* mutex)
|
||||||
|
@ -1602,34 +1900,6 @@ extern "C" inline void DeleteThreadLocalValue(void* value_holder) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements thread-local storage on pthreads-based systems.
|
// Implements thread-local storage on pthreads-based systems.
|
||||||
//
|
|
||||||
// // Thread 1
|
|
||||||
// ThreadLocal<int> tl(100); // 100 is the default value for each thread.
|
|
||||||
//
|
|
||||||
// // Thread 2
|
|
||||||
// tl.set(150); // Changes the value for thread 2 only.
|
|
||||||
// EXPECT_EQ(150, 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 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 for a thread by a ThreadLocal instance is deleted
|
|
||||||
// when the thread exits. Or, if the ThreadLocal instance dies in
|
|
||||||
// that thread, when the ThreadLocal dies. It's the user's
|
|
||||||
// responsibility to ensure that all other threads using a ThreadLocal
|
|
||||||
// have exited when it dies, or the per-thread objects for those
|
|
||||||
// threads will not be deleted.
|
|
||||||
//
|
|
||||||
// Google Test only uses global ThreadLocal objects. That means they
|
|
||||||
// will die after main() has returned. Therefore, no per-thread
|
|
||||||
// object managed by Google Test will be leaked as long as all threads
|
|
||||||
// using Google Test have exited when main() returns.
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class ThreadLocal {
|
class ThreadLocal {
|
||||||
public:
|
public:
|
||||||
|
@ -1694,9 +1964,9 @@ class ThreadLocal {
|
||||||
GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadLocal);
|
GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadLocal);
|
||||||
};
|
};
|
||||||
|
|
||||||
# define GTEST_IS_THREADSAFE 1
|
# endif // OS detection
|
||||||
|
|
||||||
#else // GTEST_HAS_PTHREAD
|
#else // GTEST_IS_THREADSAFE
|
||||||
|
|
||||||
// A dummy implementation of synchronization primitives (mutex, lock,
|
// A dummy implementation of synchronization primitives (mutex, lock,
|
||||||
// and thread-local variable). Necessary for compiling Google Test where
|
// and thread-local variable). Necessary for compiling Google Test where
|
||||||
|
@ -1716,6 +1986,11 @@ class Mutex {
|
||||||
|
|
||||||
# define GTEST_DEFINE_STATIC_MUTEX_(mutex) ::testing::internal::Mutex mutex
|
# define GTEST_DEFINE_STATIC_MUTEX_(mutex) ::testing::internal::Mutex mutex
|
||||||
|
|
||||||
|
// We cannot name this class MutexLock because the ctor declaration would
|
||||||
|
// conflict with a macro named MutexLock, which is defined on some
|
||||||
|
// platforms. That macro is used as a defensive measure to prevent against
|
||||||
|
// inadvertent misuses of MutexLock like "MutexLock(&mu)" rather than
|
||||||
|
// "MutexLock l(&mu)". Hence the typedef trick below.
|
||||||
class GTestMutexLock {
|
class GTestMutexLock {
|
||||||
public:
|
public:
|
||||||
explicit GTestMutexLock(Mutex*) {} // NOLINT
|
explicit GTestMutexLock(Mutex*) {} // NOLINT
|
||||||
|
@ -1736,11 +2011,7 @@ class ThreadLocal {
|
||||||
T value_;
|
T value_;
|
||||||
};
|
};
|
||||||
|
|
||||||
// The above synchronization primitives have dummy implementations.
|
#endif // GTEST_IS_THREADSAFE
|
||||||
// Therefore Google Test is not thread-safe.
|
|
||||||
# define GTEST_IS_THREADSAFE 0
|
|
||||||
|
|
||||||
#endif // GTEST_HAS_PTHREAD
|
|
||||||
|
|
||||||
// Returns the number of threads running in the process, or 0 to indicate that
|
// Returns the number of threads running in the process, or 0 to indicate that
|
||||||
// we cannot detect it.
|
// we cannot detect it.
|
||||||
|
|
|
@ -968,32 +968,6 @@ GTEST_API_ void ParseGoogleTestFlagsOnly(int* argc, wchar_t** argv);
|
||||||
// platform.
|
// platform.
|
||||||
GTEST_API_ std::string GetLastErrnoDescription();
|
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
|
// Attempts to parse a string into a positive integer pointed to by the
|
||||||
// number parameter. Returns true if that is possible.
|
// number parameter. Returns true if that is possible.
|
||||||
// GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we can use
|
// GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we can use
|
||||||
|
|
|
@ -36,14 +36,14 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#if GTEST_OS_WINDOWS_MOBILE
|
#if GTEST_OS_WINDOWS
|
||||||
# include <windows.h> // For TerminateProcess()
|
# include <windows.h>
|
||||||
#elif GTEST_OS_WINDOWS
|
|
||||||
# include <io.h>
|
# include <io.h>
|
||||||
# include <sys/stat.h>
|
# include <sys/stat.h>
|
||||||
|
# include <map> // Used in ThreadLocal.
|
||||||
#else
|
#else
|
||||||
# include <unistd.h>
|
# include <unistd.h>
|
||||||
#endif // GTEST_OS_WINDOWS_MOBILE
|
#endif // GTEST_OS_WINDOWS
|
||||||
|
|
||||||
#if GTEST_OS_MAC
|
#if GTEST_OS_MAC
|
||||||
# include <mach/mach_init.h>
|
# include <mach/mach_init.h>
|
||||||
|
@ -134,6 +134,389 @@ size_t GetThreadCount() {
|
||||||
|
|
||||||
#endif // GTEST_OS_MAC
|
#endif // GTEST_OS_MAC
|
||||||
|
|
||||||
|
#if GTEST_IS_THREADSAFE && GTEST_OS_WINDOWS
|
||||||
|
|
||||||
|
void SleepMilliseconds(int n) {
|
||||||
|
::Sleep(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
AutoHandle::AutoHandle()
|
||||||
|
: handle_(INVALID_HANDLE_VALUE) {}
|
||||||
|
|
||||||
|
AutoHandle::AutoHandle(Handle handle)
|
||||||
|
: handle_(handle) {}
|
||||||
|
|
||||||
|
AutoHandle::~AutoHandle() {
|
||||||
|
Reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
AutoHandle::Handle AutoHandle::Get() const {
|
||||||
|
return handle_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoHandle::Reset() {
|
||||||
|
Reset(INVALID_HANDLE_VALUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AutoHandle::Reset(HANDLE handle) {
|
||||||
|
// Resetting with the same handle we already own is invalid.
|
||||||
|
if (handle_ != handle) {
|
||||||
|
if (IsCloseable()) {
|
||||||
|
::CloseHandle(handle_);
|
||||||
|
}
|
||||||
|
handle_ = handle;
|
||||||
|
} else {
|
||||||
|
GTEST_CHECK_(!IsCloseable())
|
||||||
|
<< "Resetting a valid handle to itself is likely a programmer error "
|
||||||
|
"and thus not allowed.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AutoHandle::IsCloseable() const {
|
||||||
|
// Different Windows APIs may use either of these values to represent an
|
||||||
|
// invalid handle.
|
||||||
|
return handle_ != NULL && handle_ != INVALID_HANDLE_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
Notification::Notification()
|
||||||
|
: event_(::CreateEvent(NULL, // Default security attributes.
|
||||||
|
TRUE, // Do not reset automatically.
|
||||||
|
FALSE, // Initially unset.
|
||||||
|
NULL)) { // Anonymous event.
|
||||||
|
GTEST_CHECK_(event_.Get() != NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Notification::Notify() {
|
||||||
|
GTEST_CHECK_(::SetEvent(event_.Get()) != FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Notification::WaitForNotification() {
|
||||||
|
GTEST_CHECK_(
|
||||||
|
::WaitForSingleObject(event_.Get(), INFINITE) == WAIT_OBJECT_0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Mutex::Mutex()
|
||||||
|
: type_(kDynamic),
|
||||||
|
owner_thread_id_(0),
|
||||||
|
critical_section_init_phase_(0),
|
||||||
|
critical_section_(new CRITICAL_SECTION) {
|
||||||
|
::InitializeCriticalSection(critical_section_);
|
||||||
|
}
|
||||||
|
|
||||||
|
Mutex::~Mutex() {
|
||||||
|
// Static mutexes are leaked intentionally. It is not thread-safe to try
|
||||||
|
// to clean them up.
|
||||||
|
// TODO(yukawa): Switch to Slim Reader/Writer (SRW) Locks, which requires
|
||||||
|
// nothing to clean it up but is available only on Vista and later.
|
||||||
|
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa904937.aspx
|
||||||
|
if (type_ == kDynamic) {
|
||||||
|
::DeleteCriticalSection(critical_section_);
|
||||||
|
delete critical_section_;
|
||||||
|
critical_section_ = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mutex::Lock() {
|
||||||
|
ThreadSafeLazyInit();
|
||||||
|
::EnterCriticalSection(critical_section_);
|
||||||
|
owner_thread_id_ = ::GetCurrentThreadId();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mutex::Unlock() {
|
||||||
|
ThreadSafeLazyInit();
|
||||||
|
// We don't protect writing to owner_thread_id_ here, as it's the
|
||||||
|
// caller's responsibility to ensure that the current thread holds the
|
||||||
|
// mutex when this is called.
|
||||||
|
owner_thread_id_ = 0;
|
||||||
|
::LeaveCriticalSection(critical_section_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Does nothing if the current thread holds the mutex. Otherwise, crashes
|
||||||
|
// with high probability.
|
||||||
|
void Mutex::AssertHeld() {
|
||||||
|
ThreadSafeLazyInit();
|
||||||
|
GTEST_CHECK_(owner_thread_id_ == ::GetCurrentThreadId())
|
||||||
|
<< "The current thread is not holding the mutex @" << this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initializes owner_thread_id_ and critical_section_ in static mutexes.
|
||||||
|
void Mutex::ThreadSafeLazyInit() {
|
||||||
|
// Dynamic mutexes are initialized in the constructor.
|
||||||
|
if (type_ == kStatic) {
|
||||||
|
switch (
|
||||||
|
::InterlockedCompareExchange(&critical_section_init_phase_, 1L, 0L)) {
|
||||||
|
case 0:
|
||||||
|
// If critical_section_init_phase_ was 0 before the exchange, we
|
||||||
|
// are the first to test it and need to perform the initialization.
|
||||||
|
owner_thread_id_ = 0;
|
||||||
|
critical_section_ = new CRITICAL_SECTION;
|
||||||
|
::InitializeCriticalSection(critical_section_);
|
||||||
|
// Updates the critical_section_init_phase_ to 2 to signal
|
||||||
|
// initialization complete.
|
||||||
|
GTEST_CHECK_(::InterlockedCompareExchange(
|
||||||
|
&critical_section_init_phase_, 2L, 1L) ==
|
||||||
|
1L);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
// Somebody else is already initializing the mutex; spin until they
|
||||||
|
// are done.
|
||||||
|
while (::InterlockedCompareExchange(&critical_section_init_phase_,
|
||||||
|
2L,
|
||||||
|
2L) != 2L) {
|
||||||
|
// Possibly yields the rest of the thread's time slice to other
|
||||||
|
// threads.
|
||||||
|
::Sleep(0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
break; // The mutex is already initialized and ready for use.
|
||||||
|
|
||||||
|
default:
|
||||||
|
GTEST_CHECK_(false)
|
||||||
|
<< "Unexpected value of critical_section_init_phase_ "
|
||||||
|
<< "while initializing a static mutex.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class ThreadWithParamSupport : public ThreadWithParamBase {
|
||||||
|
public:
|
||||||
|
static HANDLE CreateThread(Runnable* runnable,
|
||||||
|
Notification* thread_can_start) {
|
||||||
|
ThreadMainParam* param = new ThreadMainParam(runnable, thread_can_start);
|
||||||
|
DWORD thread_id;
|
||||||
|
// TODO(yukawa): Consider to use _beginthreadex instead.
|
||||||
|
HANDLE thread_handle = ::CreateThread(
|
||||||
|
NULL, // Default security.
|
||||||
|
0, // Default stack size.
|
||||||
|
&ThreadWithParamSupport::ThreadMain,
|
||||||
|
param, // Parameter to ThreadMainStatic
|
||||||
|
0x0, // Default creation flags.
|
||||||
|
&thread_id); // Need a valid pointer for the call to work under Win98.
|
||||||
|
GTEST_CHECK_(thread_handle != NULL) << "CreateThread failed with error "
|
||||||
|
<< ::GetLastError() << ".";
|
||||||
|
if (thread_handle == NULL) {
|
||||||
|
delete param;
|
||||||
|
}
|
||||||
|
return thread_handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct ThreadMainParam {
|
||||||
|
ThreadMainParam(Runnable* runnable, Notification* thread_can_start)
|
||||||
|
: runnable_(runnable),
|
||||||
|
thread_can_start_(thread_can_start) {
|
||||||
|
}
|
||||||
|
scoped_ptr<Runnable> runnable_;
|
||||||
|
// Does not own.
|
||||||
|
Notification* thread_can_start_;
|
||||||
|
};
|
||||||
|
|
||||||
|
static DWORD WINAPI ThreadMain(void* ptr) {
|
||||||
|
// Transfers ownership.
|
||||||
|
scoped_ptr<ThreadMainParam> param(static_cast<ThreadMainParam*>(ptr));
|
||||||
|
if (param->thread_can_start_ != NULL)
|
||||||
|
param->thread_can_start_->WaitForNotification();
|
||||||
|
param->runnable_->Run();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prohibit instantiation.
|
||||||
|
ThreadWithParamSupport();
|
||||||
|
|
||||||
|
GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadWithParamSupport);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
ThreadWithParamBase::ThreadWithParamBase(Runnable *runnable,
|
||||||
|
Notification* thread_can_start)
|
||||||
|
: thread_(ThreadWithParamSupport::CreateThread(runnable,
|
||||||
|
thread_can_start)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadWithParamBase::~ThreadWithParamBase() {
|
||||||
|
Join();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThreadWithParamBase::Join() {
|
||||||
|
GTEST_CHECK_(::WaitForSingleObject(thread_.Get(), INFINITE) == WAIT_OBJECT_0)
|
||||||
|
<< "Failed to join the thread with error " << ::GetLastError() << ".";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maps a thread to a set of ThreadIdToThreadLocals that have values
|
||||||
|
// instantiated on that thread and notifies them when the thread exits. A
|
||||||
|
// ThreadLocal instance is expected to persist until all threads it has
|
||||||
|
// values on have terminated.
|
||||||
|
class ThreadLocalRegistryImpl {
|
||||||
|
public:
|
||||||
|
// Registers thread_local_instance as having value on the current thread.
|
||||||
|
// Returns a value that can be used to identify the thread from other threads.
|
||||||
|
static ThreadLocalValueHolderBase* GetValueOnCurrentThread(
|
||||||
|
const ThreadLocalBase* thread_local_instance) {
|
||||||
|
DWORD current_thread = ::GetCurrentThreadId();
|
||||||
|
MutexLock lock(&mutex_);
|
||||||
|
ThreadIdToThreadLocals* const thread_to_thread_locals =
|
||||||
|
GetThreadLocalsMapLocked();
|
||||||
|
ThreadIdToThreadLocals::iterator thread_local_pos =
|
||||||
|
thread_to_thread_locals->find(current_thread);
|
||||||
|
if (thread_local_pos == thread_to_thread_locals->end()) {
|
||||||
|
thread_local_pos = thread_to_thread_locals->insert(
|
||||||
|
std::make_pair(current_thread, ThreadLocalValues())).first;
|
||||||
|
StartWatcherThreadFor(current_thread);
|
||||||
|
}
|
||||||
|
ThreadLocalValues& thread_local_values = thread_local_pos->second;
|
||||||
|
ThreadLocalValues::iterator value_pos =
|
||||||
|
thread_local_values.find(thread_local_instance);
|
||||||
|
if (value_pos == thread_local_values.end()) {
|
||||||
|
value_pos =
|
||||||
|
thread_local_values
|
||||||
|
.insert(std::make_pair(
|
||||||
|
thread_local_instance,
|
||||||
|
linked_ptr<ThreadLocalValueHolderBase>(
|
||||||
|
thread_local_instance->NewValueForCurrentThread())))
|
||||||
|
.first;
|
||||||
|
}
|
||||||
|
return value_pos->second.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void OnThreadLocalDestroyed(
|
||||||
|
const ThreadLocalBase* thread_local_instance) {
|
||||||
|
std::vector<linked_ptr<ThreadLocalValueHolderBase> > value_holders;
|
||||||
|
// Clean up the ThreadLocalValues data structure while holding the lock, but
|
||||||
|
// defer the destruction of the ThreadLocalValueHolderBases.
|
||||||
|
{
|
||||||
|
MutexLock lock(&mutex_);
|
||||||
|
ThreadIdToThreadLocals* const thread_to_thread_locals =
|
||||||
|
GetThreadLocalsMapLocked();
|
||||||
|
for (ThreadIdToThreadLocals::iterator it =
|
||||||
|
thread_to_thread_locals->begin();
|
||||||
|
it != thread_to_thread_locals->end();
|
||||||
|
++it) {
|
||||||
|
ThreadLocalValues& thread_local_values = it->second;
|
||||||
|
ThreadLocalValues::iterator value_pos =
|
||||||
|
thread_local_values.find(thread_local_instance);
|
||||||
|
if (value_pos != thread_local_values.end()) {
|
||||||
|
value_holders.push_back(value_pos->second);
|
||||||
|
thread_local_values.erase(value_pos);
|
||||||
|
// This 'if' can only be successful at most once, so theoretically we
|
||||||
|
// could break out of the loop here, but we don't bother doing so.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Outside the lock, let the destructor for 'value_holders' deallocate the
|
||||||
|
// ThreadLocalValueHolderBases.
|
||||||
|
}
|
||||||
|
|
||||||
|
static void OnThreadExit(DWORD thread_id) {
|
||||||
|
GTEST_CHECK_(thread_id != 0) << ::GetLastError();
|
||||||
|
std::vector<linked_ptr<ThreadLocalValueHolderBase> > value_holders;
|
||||||
|
// Clean up the ThreadIdToThreadLocals data structure while holding the
|
||||||
|
// lock, but defer the destruction of the ThreadLocalValueHolderBases.
|
||||||
|
{
|
||||||
|
MutexLock lock(&mutex_);
|
||||||
|
ThreadIdToThreadLocals* const thread_to_thread_locals =
|
||||||
|
GetThreadLocalsMapLocked();
|
||||||
|
ThreadIdToThreadLocals::iterator thread_local_pos =
|
||||||
|
thread_to_thread_locals->find(thread_id);
|
||||||
|
if (thread_local_pos != thread_to_thread_locals->end()) {
|
||||||
|
ThreadLocalValues& thread_local_values = thread_local_pos->second;
|
||||||
|
for (ThreadLocalValues::iterator value_pos =
|
||||||
|
thread_local_values.begin();
|
||||||
|
value_pos != thread_local_values.end();
|
||||||
|
++value_pos) {
|
||||||
|
value_holders.push_back(value_pos->second);
|
||||||
|
}
|
||||||
|
thread_to_thread_locals->erase(thread_local_pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Outside the lock, let the destructor for 'value_holders' deallocate the
|
||||||
|
// ThreadLocalValueHolderBases.
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// In a particular thread, maps a ThreadLocal object to its value.
|
||||||
|
typedef std::map<const ThreadLocalBase*,
|
||||||
|
linked_ptr<ThreadLocalValueHolderBase> > ThreadLocalValues;
|
||||||
|
// Stores all ThreadIdToThreadLocals having values in a thread, indexed by
|
||||||
|
// thread's ID.
|
||||||
|
typedef std::map<DWORD, ThreadLocalValues> ThreadIdToThreadLocals;
|
||||||
|
|
||||||
|
// Holds the thread id and thread handle that we pass from
|
||||||
|
// StartWatcherThreadFor to WatcherThreadFunc.
|
||||||
|
typedef std::pair<DWORD, HANDLE> ThreadIdAndHandle;
|
||||||
|
|
||||||
|
static void StartWatcherThreadFor(DWORD thread_id) {
|
||||||
|
// The returned handle will be kept in thread_map and closed by
|
||||||
|
// watcher_thread in WatcherThreadFunc.
|
||||||
|
HANDLE thread = ::OpenThread(SYNCHRONIZE | THREAD_QUERY_INFORMATION,
|
||||||
|
FALSE,
|
||||||
|
thread_id);
|
||||||
|
GTEST_CHECK_(thread != NULL);
|
||||||
|
// We need to to pass a valid thread ID pointer into CreateThread for it
|
||||||
|
// to work correctly under Win98.
|
||||||
|
DWORD watcher_thread_id;
|
||||||
|
HANDLE watcher_thread = ::CreateThread(
|
||||||
|
NULL, // Default security.
|
||||||
|
0, // Default stack size
|
||||||
|
&ThreadLocalRegistryImpl::WatcherThreadFunc,
|
||||||
|
reinterpret_cast<LPVOID>(new ThreadIdAndHandle(thread_id, thread)),
|
||||||
|
CREATE_SUSPENDED,
|
||||||
|
&watcher_thread_id);
|
||||||
|
GTEST_CHECK_(watcher_thread != NULL);
|
||||||
|
// Give the watcher thread the same priority as ours to avoid being
|
||||||
|
// blocked by it.
|
||||||
|
::SetThreadPriority(watcher_thread,
|
||||||
|
::GetThreadPriority(::GetCurrentThread()));
|
||||||
|
::ResumeThread(watcher_thread);
|
||||||
|
::CloseHandle(watcher_thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Monitors exit from a given thread and notifies those
|
||||||
|
// ThreadIdToThreadLocals about thread termination.
|
||||||
|
static DWORD WINAPI WatcherThreadFunc(LPVOID param) {
|
||||||
|
const ThreadIdAndHandle* tah =
|
||||||
|
reinterpret_cast<const ThreadIdAndHandle*>(param);
|
||||||
|
GTEST_CHECK_(
|
||||||
|
::WaitForSingleObject(tah->second, INFINITE) == WAIT_OBJECT_0);
|
||||||
|
OnThreadExit(tah->first);
|
||||||
|
::CloseHandle(tah->second);
|
||||||
|
delete tah;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns map of thread local instances.
|
||||||
|
static ThreadIdToThreadLocals* GetThreadLocalsMapLocked() {
|
||||||
|
mutex_.AssertHeld();
|
||||||
|
static ThreadIdToThreadLocals* map = new ThreadIdToThreadLocals;
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Protects access to GetThreadLocalsMapLocked() and its return value.
|
||||||
|
static Mutex mutex_;
|
||||||
|
// Protects access to GetThreadMapLocked() and its return value.
|
||||||
|
static Mutex thread_map_mutex_;
|
||||||
|
};
|
||||||
|
|
||||||
|
Mutex ThreadLocalRegistryImpl::mutex_(Mutex::kStaticMutex);
|
||||||
|
Mutex ThreadLocalRegistryImpl::thread_map_mutex_(Mutex::kStaticMutex);
|
||||||
|
|
||||||
|
ThreadLocalValueHolderBase* ThreadLocalRegistry::GetValueOnCurrentThread(
|
||||||
|
const ThreadLocalBase* thread_local_instance) {
|
||||||
|
return ThreadLocalRegistryImpl::GetValueOnCurrentThread(
|
||||||
|
thread_local_instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThreadLocalRegistry::OnThreadLocalDestroyed(
|
||||||
|
const ThreadLocalBase* thread_local_instance) {
|
||||||
|
ThreadLocalRegistryImpl::OnThreadLocalDestroyed(thread_local_instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // GTEST_IS_THREADSAFE && GTEST_OS_WINDOWS
|
||||||
|
|
||||||
#if GTEST_USES_POSIX_RE
|
#if GTEST_USES_POSIX_RE
|
||||||
|
|
||||||
// Implements RE. Currently only needed for death tests.
|
// Implements RE. Currently only needed for death tests.
|
||||||
|
|
28
src/gtest.cc
28
src/gtest.cc
|
@ -3219,27 +3219,23 @@ std::string FormatTimeInMillisAsSeconds(TimeInMillis ms) {
|
||||||
// Converts the given epoch time in milliseconds to a date string in the ISO
|
// Converts the given epoch time in milliseconds to a date string in the ISO
|
||||||
// 8601 format, without the timezone information.
|
// 8601 format, without the timezone information.
|
||||||
std::string FormatEpochTimeInMillisAsIso8601(TimeInMillis ms) {
|
std::string FormatEpochTimeInMillisAsIso8601(TimeInMillis ms) {
|
||||||
// Using non-reentrant version as localtime_r is not portable.
|
|
||||||
time_t seconds = static_cast<time_t>(ms / 1000);
|
time_t seconds = static_cast<time_t>(ms / 1000);
|
||||||
|
struct tm time_struct;
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
# pragma warning(push) // Saves the current warning state.
|
if (localtime_s(&time_struct, &seconds) != 0)
|
||||||
# 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.
|
|
||||||
#else
|
|
||||||
const struct tm* const time_struct = localtime(&seconds); // NOLINT
|
|
||||||
#endif
|
|
||||||
if (time_struct == NULL)
|
|
||||||
return ""; // Invalid ms value
|
return ""; // Invalid ms value
|
||||||
|
#else
|
||||||
|
if (localtime_r(&seconds, &time_struct) == NULL)
|
||||||
|
return ""; // Invalid ms value
|
||||||
|
#endif
|
||||||
|
|
||||||
// YYYY-MM-DDThh:mm:ss
|
// YYYY-MM-DDThh:mm:ss
|
||||||
return StreamableToString(time_struct->tm_year + 1900) + "-" +
|
return StreamableToString(time_struct.tm_year + 1900) + "-" +
|
||||||
String::FormatIntWidth2(time_struct->tm_mon + 1) + "-" +
|
String::FormatIntWidth2(time_struct.tm_mon + 1) + "-" +
|
||||||
String::FormatIntWidth2(time_struct->tm_mday) + "T" +
|
String::FormatIntWidth2(time_struct.tm_mday) + "T" +
|
||||||
String::FormatIntWidth2(time_struct->tm_hour) + ":" +
|
String::FormatIntWidth2(time_struct.tm_hour) + ":" +
|
||||||
String::FormatIntWidth2(time_struct->tm_min) + ":" +
|
String::FormatIntWidth2(time_struct.tm_min) + ":" +
|
||||||
String::FormatIntWidth2(time_struct->tm_sec);
|
String::FormatIntWidth2(time_struct.tm_sec);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Streams an XML CDATA section, escaping invalid CDATA sequences as needed.
|
// Streams an XML CDATA section, escaping invalid CDATA sequences as needed.
|
||||||
|
|
|
@ -699,7 +699,10 @@ TEST_F(TestForDeathTest, ExpectDebugDeathDoesNotAbort) {
|
||||||
|
|
||||||
void AssertDebugDeathHelper(bool* aborted) {
|
void AssertDebugDeathHelper(bool* aborted) {
|
||||||
*aborted = true;
|
*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;
|
*aborted = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -712,6 +715,69 @@ TEST_F(TestForDeathTest, AssertDebugDeathAborts) {
|
||||||
EXPECT_TRUE(aborted);
|
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
|
# endif // _NDEBUG
|
||||||
|
|
||||||
// Tests the *_EXIT family of macros, using a variety of predicates.
|
// Tests the *_EXIT family of macros, using a variety of predicates.
|
||||||
|
|
|
@ -1062,11 +1062,13 @@ class AtomicCounterWithMutex {
|
||||||
MutexLock lock(mutex_);
|
MutexLock lock(mutex_);
|
||||||
int temp = value_;
|
int temp = value_;
|
||||||
{
|
{
|
||||||
// Locking a mutex puts up a memory barrier, preventing reads and
|
// We need to put up a memory barrier to prevent reads and writes to
|
||||||
// writes to value_ rearranged when observed from other threads.
|
// value_ rearranged with the call to SleepMilliseconds when observed
|
||||||
//
|
// from other threads.
|
||||||
// We cannot use Mutex and MutexLock here or rely on their memory
|
#if GTEST_HAS_PTHREAD
|
||||||
// barrier functionality as we are testing them here.
|
// 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;
|
pthread_mutex_t memory_barrier_mutex;
|
||||||
GTEST_CHECK_POSIX_SUCCESS_(
|
GTEST_CHECK_POSIX_SUCCESS_(
|
||||||
pthread_mutex_init(&memory_barrier_mutex, NULL));
|
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_unlock(&memory_barrier_mutex));
|
||||||
GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_destroy(&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;
|
value_ = temp + 1;
|
||||||
}
|
}
|
||||||
|
@ -1145,27 +1156,76 @@ TEST(ThreadLocalTest, ParameterizedConstructorSetsDefault) {
|
||||||
EXPECT_STREQ("foo", result.c_str());
|
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
|
// DestructorTracker keeps track of whether its instances have been
|
||||||
// destroyed.
|
// destroyed.
|
||||||
static std::vector<bool> g_destroyed;
|
|
||||||
|
|
||||||
class DestructorTracker {
|
class DestructorTracker {
|
||||||
public:
|
public:
|
||||||
DestructorTracker() : index_(GetNewIndex()) {}
|
DestructorTracker() : index_(GetNewIndex()) {}
|
||||||
DestructorTracker(const DestructorTracker& /* rhs */)
|
DestructorTracker(const DestructorTracker& /* rhs */)
|
||||||
: index_(GetNewIndex()) {}
|
: index_(GetNewIndex()) {}
|
||||||
~DestructorTracker() {
|
~DestructorTracker() {
|
||||||
// We never access g_destroyed concurrently, so we don't need to
|
// We never access DestructorCall::List() concurrently, so we don't need
|
||||||
// protect the write operation under a mutex.
|
// to protect this acccess with a mutex.
|
||||||
g_destroyed[index_] = true;
|
DestructorCall::List()[index_]->ReportDestroyed();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static int GetNewIndex() {
|
static int GetNewIndex() {
|
||||||
g_destroyed.push_back(false);
|
DestructorCall::List().push_back(new DestructorCall);
|
||||||
return g_destroyed.size() - 1;
|
return DestructorCall::List().size() - 1;
|
||||||
}
|
}
|
||||||
const int index_;
|
const int index_;
|
||||||
|
|
||||||
|
GTEST_DISALLOW_ASSIGN_(DestructorTracker);
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef ThreadLocal<DestructorTracker>* ThreadParam;
|
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
|
// Tests that when a ThreadLocal object dies in a thread, it destroys
|
||||||
// the managed object for that thread.
|
// the managed object for that thread.
|
||||||
TEST(ThreadLocalTest, DestroysManagedObjectForOwnThreadWhenDying) {
|
TEST(ThreadLocalTest, DestroysManagedObjectForOwnThreadWhenDying) {
|
||||||
g_destroyed.clear();
|
DestructorCall::ResetList();
|
||||||
|
|
||||||
{
|
{
|
||||||
// The next line default constructs a DestructorTracker object as
|
// The next line default constructs a DestructorTracker object as
|
||||||
// the default value of objects managed by thread_local_tracker.
|
// the default value of objects managed by thread_local_tracker.
|
||||||
ThreadLocal<DestructorTracker> thread_local_tracker;
|
ThreadLocal<DestructorTracker> thread_local_tracker;
|
||||||
ASSERT_EQ(1U, g_destroyed.size());
|
ASSERT_EQ(1U, DestructorCall::List().size());
|
||||||
ASSERT_FALSE(g_destroyed[0]);
|
ASSERT_FALSE(DestructorCall::List()[0]->CheckDestroyed());
|
||||||
|
|
||||||
// This creates another DestructorTracker object for the main thread.
|
// This creates another DestructorTracker object for the main thread.
|
||||||
thread_local_tracker.get();
|
thread_local_tracker.get();
|
||||||
ASSERT_EQ(2U, g_destroyed.size());
|
ASSERT_EQ(2U, DestructorCall::List().size());
|
||||||
ASSERT_FALSE(g_destroyed[0]);
|
ASSERT_FALSE(DestructorCall::List()[0]->CheckDestroyed());
|
||||||
ASSERT_FALSE(g_destroyed[1]);
|
ASSERT_FALSE(DestructorCall::List()[1]->CheckDestroyed());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now thread_local_tracker has died. It should have destroyed both the
|
// Now thread_local_tracker has died. It should have destroyed both the
|
||||||
// default value shared by all threads and the value for the main
|
// default value shared by all threads and the value for the main
|
||||||
// thread.
|
// thread.
|
||||||
ASSERT_EQ(2U, g_destroyed.size());
|
ASSERT_EQ(2U, DestructorCall::List().size());
|
||||||
EXPECT_TRUE(g_destroyed[0]);
|
EXPECT_TRUE(DestructorCall::List()[0]->CheckDestroyed());
|
||||||
EXPECT_TRUE(g_destroyed[1]);
|
EXPECT_TRUE(DestructorCall::List()[1]->CheckDestroyed());
|
||||||
|
|
||||||
g_destroyed.clear();
|
DestructorCall::ResetList();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests that when a thread exits, the thread-local object for that
|
// Tests that when a thread exits, the thread-local object for that
|
||||||
// thread is destroyed.
|
// thread is destroyed.
|
||||||
TEST(ThreadLocalTest, DestroysManagedObjectAtThreadExit) {
|
TEST(ThreadLocalTest, DestroysManagedObjectAtThreadExit) {
|
||||||
g_destroyed.clear();
|
DestructorCall::ResetList();
|
||||||
|
|
||||||
{
|
{
|
||||||
// The next line default constructs a DestructorTracker object as
|
// The next line default constructs a DestructorTracker object as
|
||||||
// the default value of objects managed by thread_local_tracker.
|
// the default value of objects managed by thread_local_tracker.
|
||||||
ThreadLocal<DestructorTracker> thread_local_tracker;
|
ThreadLocal<DestructorTracker> thread_local_tracker;
|
||||||
ASSERT_EQ(1U, g_destroyed.size());
|
ASSERT_EQ(1U, DestructorCall::List().size());
|
||||||
ASSERT_FALSE(g_destroyed[0]);
|
ASSERT_FALSE(DestructorCall::List()[0]->CheckDestroyed());
|
||||||
|
|
||||||
// This creates another DestructorTracker object in the new thread.
|
// This creates another DestructorTracker object in the new thread.
|
||||||
ThreadWithParam<ThreadParam> thread(
|
ThreadWithParam<ThreadParam> thread(
|
||||||
&CallThreadLocalGet, &thread_local_tracker, NULL);
|
&CallThreadLocalGet, &thread_local_tracker, NULL);
|
||||||
thread.Join();
|
thread.Join();
|
||||||
|
|
||||||
// Now the new thread has exited. The per-thread object for it
|
// The thread has exited, and we should have another DestroyedTracker
|
||||||
// should have been destroyed.
|
// instance created for it. But it may not have been destroyed yet.
|
||||||
ASSERT_EQ(2U, g_destroyed.size());
|
// The instance for the main thread should still persist.
|
||||||
ASSERT_FALSE(g_destroyed[0]);
|
ASSERT_EQ(2U, DestructorCall::List().size());
|
||||||
ASSERT_TRUE(g_destroyed[1]);
|
ASSERT_FALSE(DestructorCall::List()[0]->CheckDestroyed());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now thread_local_tracker has died. The default value should have been
|
// The thread has exited and thread_local_tracker has died. The default
|
||||||
// destroyed too.
|
// value should have been destroyed too.
|
||||||
ASSERT_EQ(2U, g_destroyed.size());
|
ASSERT_EQ(2U, DestructorCall::List().size());
|
||||||
EXPECT_TRUE(g_destroyed[0]);
|
EXPECT_TRUE(DestructorCall::List()[0]->CheckDestroyed());
|
||||||
EXPECT_TRUE(g_destroyed[1]);
|
EXPECT_TRUE(DestructorCall::List()[1]->CheckDestroyed());
|
||||||
|
|
||||||
g_destroyed.clear();
|
DestructorCall::ResetList();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ThreadLocalTest, ThreadLocalMutationsAffectOnlyCurrentThread) {
|
TEST(ThreadLocalTest, ThreadLocalMutationsAffectOnlyCurrentThread) {
|
||||||
|
@ -1249,5 +1309,15 @@ TEST(ThreadLocalTest, ThreadLocalMutationsAffectOnlyCurrentThread) {
|
||||||
|
|
||||||
#endif // GTEST_IS_THREADSAFE
|
#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 internal
|
||||||
} // namespace testing
|
} // namespace testing
|
||||||
|
|
|
@ -252,8 +252,8 @@ SUPPORTS_STACK_TRACES = False
|
||||||
|
|
||||||
CAN_GENERATE_GOLDEN_FILE = (SUPPORTS_DEATH_TESTS and
|
CAN_GENERATE_GOLDEN_FILE = (SUPPORTS_DEATH_TESTS and
|
||||||
SUPPORTS_TYPED_TESTS and
|
SUPPORTS_TYPED_TESTS and
|
||||||
SUPPORTS_THREADS)
|
SUPPORTS_THREADS and
|
||||||
|
not IS_WINDOWS)
|
||||||
|
|
||||||
class GTestOutputTest(gtest_test_utils.TestCase):
|
class GTestOutputTest(gtest_test_utils.TestCase):
|
||||||
def RemoveUnsupportedTests(self, test_output):
|
def RemoveUnsupportedTests(self, test_output):
|
||||||
|
|
Loading…
Reference in New Issue
Block a user