Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
G
googletest
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Chen Yisong
googletest
Commits
a6340420
Commit
a6340420
authored
Mar 24, 2014
by
kosak
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Implement threading support for gtest on Windows.
Also, stop using localtime(). Instead, use localtime_r() on most systems, localtime_s() on Windows.
parent
ffea2d60
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
911 additions
and
151 deletions
+911
-151
gtest-port.h
include/gtest/internal/gtest-port.h
+338
-67
gtest-internal-inl.h
src/gtest-internal-inl.h
+0
-26
gtest-port.cc
src/gtest-port.cc
+387
-4
gtest.cc
src/gtest.cc
+11
-15
gtest-death-test_test.cc
test/gtest-death-test_test.cc
+67
-1
gtest-port_test.cc
test/gtest-port_test.cc
+106
-36
gtest_output_test.py
test/gtest_output_test.py
+2
-2
No files found.
include/gtest/internal/gtest-port.h
View file @
a6340420
...
...
@@ -379,16 +379,23 @@
// Brings in definitions for functions used in the testing::internal::posix
// namespace (read, write, close, chdir, isatty, stat). We do not currently
// 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
// is not the case, we need to include headers that provide the functions
// mentioned above.
# include <unistd.h>
# include <strings.h>
#elif !GTEST_OS_WINDOWS_MOBILE
# include <direct.h>
# include <io.h>
#endif
#endif // GTEST_OS_WINDOWS
#if GTEST_OS_LINUX_ANDROID
// Used to define __ANDROID_API__ matching the target NDK API level.
...
...
@@ -871,6 +878,9 @@ using ::std::tuple_size;
# define GTEST_HAS_SEH 0
# endif
#define GTEST_IS_THREADSAFE \
(GTEST_OS_WINDOWS || GTEST_HAS_PTHREAD)
#endif // GTEST_HAS_SEH
#ifdef _MSC_VER
...
...
@@ -1340,12 +1350,11 @@ extern ::std::vector<testing::internal::string> g_argvs;
#endif // GTEST_HAS_DEATH_TEST
// Defines synchronization primitives.
#if GTEST_HAS_PTHREAD
// Sleeps for (roughly) n milli-seconds. This function is only for
// testing Google Test's own constructs. Don't use it in user tests,
// either directly or indirectly.
#if GTEST_IS_THREADSAFE
# if GTEST_HAS_PTHREAD
// Sleeps for (roughly) n milliseconds. This function is only for testing
// Google Test's own constructs. Don't use it in user tests, either
// directly or indirectly.
inline
void
SleepMilliseconds
(
int
n
)
{
const
timespec
time
=
{
0
,
// 0 seconds.
...
...
@@ -1353,7 +1362,10 @@ inline void SleepMilliseconds(int n) {
};
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
// threads until notified. Instances of this class must be created
// and destroyed in the controller thread.
...
...
@@ -1397,6 +1409,62 @@ class 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.
// Consequently, it cannot select a correct instantiation of ThreadWithParam
// in order to call its Run(). Introducing ThreadWithParamBase as a
...
...
@@ -1434,10 +1502,9 @@ extern "C" inline void* ThreadFuncWithCLinkage(void* thread) {
template
<
typename
T
>
class
ThreadWithParam
:
public
ThreadWithParamBase
{
public
:
typedef
void
(
*
UserThreadFunc
)
(
T
);
typedef
void
UserThreadFunc
(
T
);
ThreadWithParam
(
UserThreadFunc
func
,
T
param
,
Notification
*
thread_can_start
)
ThreadWithParam
(
UserThreadFunc
*
func
,
T
param
,
Notification
*
thread_can_start
)
:
func_
(
func
),
param_
(
param
),
thread_can_start_
(
thread_can_start
),
...
...
@@ -1464,7 +1531,7 @@ class ThreadWithParam : public ThreadWithParamBase {
}
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.
// When non-NULL, used to block execution until the controller thread
// notifies.
...
...
@@ -1474,26 +1541,255 @@ class ThreadWithParam : public ThreadWithParamBase {
GTEST_DISALLOW_COPY_AND_ASSIGN_
(
ThreadWithParam
);
};
# endif // GTEST_HAS_PTHREAD && !GTEST_OS_WINDOWS_MINGW
// MutexBase and Mutex implement mutex on pthreads-based platforms. They
// are used in conjunction with class MutexLock:
# if 0 // OS detection
# elif GTEST_OS_WINDOWS
// Mutex implements mutex on Windows platforms. It is used in conjunction
// with class MutexLock:
//
// 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 MutexBase directly. Instead, write
// the following to define a static mutex:
// MutexLock lock(&mutex); // Acquires the mutex and releases it at the
// // end of the current scope.
//
// A static Mutex *must* be defined or declared using one of the following
// macros:
// GTEST_DEFINE_STATIC_MUTEX_(g_some_mutex);
// GTEST_DECLARE_STATIC_MUTEX_(g_some_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.
//
// You can forward declare a static mutex like this:
// // Thread 1
// ThreadLocal<int> tl(100); // 100 is the default value for each thread.
//
// GTEST_DECLARE_STATIC_MUTEX_(g_some_mutex);
// // Thread 2
// tl.set(150); // Changes the value for thread 2 only.
// EXPECT_EQ(150, tl.get());
//
// To create a dynamic mutex, just define an object of type Mutex.
// // 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
{
public
:
// Acquires this mutex.
...
...
@@ -1538,8 +1834,8 @@ class MutexBase {
};
// Forward-declares a static mutex.
# define GTEST_DECLARE_STATIC_MUTEX_(mutex) \
extern ::testing::internal::MutexBase mutex
#
define GTEST_DECLARE_STATIC_MUTEX_(mutex) \
extern ::testing::internal::MutexBase mutex
// Defines and statically (i.e. at link time) initializes a static mutex.
// The initialization list here does not explicitly initialize each field,
...
...
@@ -1547,8 +1843,8 @@ class MutexBase {
// particular, the owner_ field (a pthread_t) is not explicitly initialized.
// This allows initialization to work whether pthread_t is a scalar or struct.
// The flag -Wmissing-field-initializers must not be specified for this to work.
# define GTEST_DEFINE_STATIC_MUTEX_(mutex) \
::testing::internal::MutexBase mutex = { PTHREAD_MUTEX_INITIALIZER, false }
#
define GTEST_DEFINE_STATIC_MUTEX_(mutex) \
::testing::internal::MutexBase mutex = { PTHREAD_MUTEX_INITIALIZER, false }
// The Mutex class can only be used for mutexes created at runtime. It
// shares its API with MutexBase otherwise.
...
...
@@ -1566,9 +1862,11 @@ class Mutex : public MutexBase {
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
// 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
{
public
:
explicit
GTestMutexLock
(
MutexBase
*
mutex
)
...
...
@@ -1602,34 +1900,6 @@ extern "C" inline void DeleteThreadLocalValue(void* value_holder) {
}
// 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
>
class
ThreadLocal
{
public
:
...
...
@@ -1694,9 +1964,9 @@ class 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,
// 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
// 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
*
)
{}
// NOLINT
...
...
@@ -1736,11 +2011,7 @@ class ThreadLocal {
T
value_
;
};
// The above synchronization primitives have dummy implementations.
// Therefore Google Test is not thread-safe.
# define GTEST_IS_THREADSAFE 0
#endif // GTEST_HAS_PTHREAD
#endif // GTEST_IS_THREADSAFE
// Returns the number of threads running in the process, or 0 to indicate that
// we cannot detect it.
...
...
src/gtest-internal-inl.h
View file @
a6340420
...
...
@@ -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
...
...
src/gtest-port.cc
View file @
a6340420
...
...
@@ -36,14 +36,14 @@
#include <stdio.h>
#include <string.h>
#if GTEST_OS_WINDOWS_MOBILE
# include <windows.h> // For TerminateProcess()
#elif GTEST_OS_WINDOWS
#if GTEST_OS_WINDOWS
# include <windows.h>
# include <io.h>
# include <sys/stat.h>
# include <map> // Used in ThreadLocal.
#else
# include <unistd.h>
#endif // GTEST_OS_WINDOWS
_MOBILE
#endif // GTEST_OS_WINDOWS
#if GTEST_OS_MAC
# include <mach/mach_init.h>
...
...
@@ -134,6 +134,389 @@ size_t GetThreadCount() {
#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
// Implements RE. Currently only needed for death tests.
...
...
src/gtest.cc
View file @
a6340420
...
...
@@ -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.
...
...
test/gtest-death-test_test.cc
View file @
a6340420
...
...
@@ -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.
...
...
test/gtest-port_test.cc
View file @
a6340420
...
...
@@ -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
test/gtest_output_test.py
View file @
a6340420
...
...
@@ -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
):
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment