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
This commit is contained in:
parent
ec7b38672c
commit
d3a29ff624
@ -587,9 +587,11 @@ class ThreadLocalRegistryImpl {
|
|||||||
// thread's ID.
|
// thread's ID.
|
||||||
typedef std::map<DWORD, ThreadLocalValues> ThreadIdToThreadLocals;
|
typedef std::map<DWORD, ThreadLocalValues> ThreadIdToThreadLocals;
|
||||||
|
|
||||||
// Holds the thread id and thread handle that we pass from
|
struct WatcherThreadParams {
|
||||||
// StartWatcherThreadFor to WatcherThreadFunc.
|
DWORD thread_id;
|
||||||
typedef std::pair<DWORD, HANDLE> ThreadIdAndHandle;
|
HANDLE handle;
|
||||||
|
Notification has_initialized;
|
||||||
|
};
|
||||||
|
|
||||||
static void StartWatcherThreadFor(DWORD thread_id) {
|
static void StartWatcherThreadFor(DWORD thread_id) {
|
||||||
// The returned handle will be kept in thread_map and closed by
|
// The returned handle will be kept in thread_map and closed by
|
||||||
@ -597,6 +599,11 @@ class ThreadLocalRegistryImpl {
|
|||||||
HANDLE thread =
|
HANDLE thread =
|
||||||
::OpenThread(SYNCHRONIZE | THREAD_QUERY_INFORMATION, FALSE, thread_id);
|
::OpenThread(SYNCHRONIZE | THREAD_QUERY_INFORMATION, FALSE, thread_id);
|
||||||
GTEST_CHECK_(thread != nullptr);
|
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
|
// We need to pass a valid thread ID pointer into CreateThread for it
|
||||||
// to work correctly under Win98.
|
// to work correctly under Win98.
|
||||||
DWORD watcher_thread_id;
|
DWORD watcher_thread_id;
|
||||||
@ -604,7 +611,7 @@ class ThreadLocalRegistryImpl {
|
|||||||
nullptr, // Default security.
|
nullptr, // Default security.
|
||||||
0, // Default stack size
|
0, // Default stack size
|
||||||
&ThreadLocalRegistryImpl::WatcherThreadFunc,
|
&ThreadLocalRegistryImpl::WatcherThreadFunc,
|
||||||
reinterpret_cast<LPVOID>(new ThreadIdAndHandle(thread_id, thread)),
|
reinterpret_cast<LPVOID>(watcher_thread_params),
|
||||||
CREATE_SUSPENDED, &watcher_thread_id);
|
CREATE_SUSPENDED, &watcher_thread_id);
|
||||||
GTEST_CHECK_(watcher_thread != nullptr)
|
GTEST_CHECK_(watcher_thread != nullptr)
|
||||||
<< "CreateThread failed with error " << ::GetLastError() << ".";
|
<< "CreateThread failed with error " << ::GetLastError() << ".";
|
||||||
@ -614,17 +621,25 @@ class ThreadLocalRegistryImpl {
|
|||||||
::GetThreadPriority(::GetCurrentThread()));
|
::GetThreadPriority(::GetCurrentThread()));
|
||||||
::ResumeThread(watcher_thread);
|
::ResumeThread(watcher_thread);
|
||||||
::CloseHandle(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
|
// Monitors exit from a given thread and notifies those
|
||||||
// ThreadIdToThreadLocals about thread termination.
|
// ThreadIdToThreadLocals about thread termination.
|
||||||
static DWORD WINAPI WatcherThreadFunc(LPVOID param) {
|
static DWORD WINAPI WatcherThreadFunc(LPVOID param) {
|
||||||
const ThreadIdAndHandle* tah =
|
WatcherThreadParams* watcher_thread_params =
|
||||||
reinterpret_cast<const ThreadIdAndHandle*>(param);
|
reinterpret_cast<WatcherThreadParams*>(param);
|
||||||
GTEST_CHECK_(::WaitForSingleObject(tah->second, INFINITE) == WAIT_OBJECT_0);
|
watcher_thread_params->has_initialized.Notify();
|
||||||
OnThreadExit(tah->first);
|
GTEST_CHECK_(::WaitForSingleObject(watcher_thread_params->handle,
|
||||||
::CloseHandle(tah->second);
|
INFINITE) == WAIT_OBJECT_0);
|
||||||
delete tah;
|
OnThreadExit(watcher_thread_params->thread_id);
|
||||||
|
::CloseHandle(watcher_thread_params->handle);
|
||||||
|
delete watcher_thread_params;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user