aboutsummaryrefslogtreecommitdiff
path: root/googletest
diff options
context:
space:
mode:
authorDerek Mauro <dmauro@google.com>2024-04-03 13:21:06 -0700
committerCopybara-Service <copybara-worker@google.com>2024-04-03 13:21:58 -0700
commitd3a29ff624e0984a40a60dbc1f03ac74b8d03658 (patch)
tree87b342007dbbb096230f003b1e8b28fa431df0a0 /googletest
parentec7b38672c04c8af3cfb078214edf6751b841014 (diff)
downloadgoogletest-d3a29ff624e0984a40a60dbc1f03ac74b8d03658.zip
googletest-d3a29ff624e0984a40a60dbc1f03ac74b8d03658.tar.gz
googletest-d3a29ff624e0984a40a60dbc1f03ac74b8d03658.tar.bz2
Fix a race condition between the Watcher thread and the main thread
during program exit A race condition exist between the Watcher thread and main(). A case was found where the Watcher thread does not get execution time before the main function returns and calls atexit(). At that point the Watcher thread started runing tls_init() code while the main thread was shutting down. This resulted in rare crashes and deadlocks. Fixes #4493 Closes #4494 PiperOrigin-RevId: 621619768 Change-Id: I66f00d8f0f3c37f9937c6d13890f7fa10038256d
Diffstat (limited to 'googletest')
-rw-r--r--googletest/src/gtest-port.cc35
1 files changed, 25 insertions, 10 deletions
diff --git a/googletest/src/gtest-port.cc b/googletest/src/gtest-port.cc
index 3bb7dd4..aa33725 100644
--- a/googletest/src/gtest-port.cc
+++ b/googletest/src/gtest-port.cc
@@ -587,9 +587,11 @@ class ThreadLocalRegistryImpl {
// 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;
+ struct WatcherThreadParams {
+ DWORD thread_id;
+ HANDLE handle;
+ Notification has_initialized;
+ };
static void StartWatcherThreadFor(DWORD thread_id) {
// The returned handle will be kept in thread_map and closed by
@@ -597,6 +599,11 @@ class ThreadLocalRegistryImpl {
HANDLE thread =
::OpenThread(SYNCHRONIZE | THREAD_QUERY_INFORMATION, FALSE, thread_id);
GTEST_CHECK_(thread != nullptr);
+
+ WatcherThreadParams* watcher_thread_params = new WatcherThreadParams;
+ watcher_thread_params->thread_id = thread_id;
+ watcher_thread_params->handle = thread;
+
// We need to pass a valid thread ID pointer into CreateThread for it
// to work correctly under Win98.
DWORD watcher_thread_id;
@@ -604,7 +611,7 @@ class ThreadLocalRegistryImpl {
nullptr, // Default security.
0, // Default stack size
&ThreadLocalRegistryImpl::WatcherThreadFunc,
- reinterpret_cast<LPVOID>(new ThreadIdAndHandle(thread_id, thread)),
+ reinterpret_cast<LPVOID>(watcher_thread_params),
CREATE_SUSPENDED, &watcher_thread_id);
GTEST_CHECK_(watcher_thread != nullptr)
<< "CreateThread failed with error " << ::GetLastError() << ".";
@@ -614,17 +621,25 @@ class ThreadLocalRegistryImpl {
::GetThreadPriority(::GetCurrentThread()));
::ResumeThread(watcher_thread);
::CloseHandle(watcher_thread);
+
+ // Wait for the watcher thread to start to avoid race conditions.
+ // One specific race condition that can happen is that we have returned
+ // from main and have started to tear down, the newly spawned watcher
+ // thread may access already-freed variables, like global shared_ptrs.
+ watcher_thread_params->has_initialized.WaitForNotification();
}
// 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;
+ WatcherThreadParams* watcher_thread_params =
+ reinterpret_cast<WatcherThreadParams*>(param);
+ watcher_thread_params->has_initialized.Notify();
+ GTEST_CHECK_(::WaitForSingleObject(watcher_thread_params->handle,
+ INFINITE) == WAIT_OBJECT_0);
+ OnThreadExit(watcher_thread_params->thread_id);
+ ::CloseHandle(watcher_thread_params->handle);
+ delete watcher_thread_params;
return 0;
}