Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Silence false positive memory leaks reported by MSVC debug CRT #892

Closed
wants to merge 5 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
52 changes: 50 additions & 2 deletions googletest/src/gtest-port.cc
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,41 @@ void Mutex::AssertHeld() {
<< "The current thread is not holding the mutex @" << this;
}

namespace {

// Use a RAII idiom to flag mem allocs that are intentionally never
// deallocated. The motivation is to silence the false positive mem leaks
// that are reported by the debug version of MS's CRT which can only detect
// if an alloc is missing a matching deallocation.
// Example:
// MemoryIsNotDeallocated memory_is_not_deallocated;
// critical_section_ = new CRITICAL_SECTION;
class MemoryIsNotDeallocated
{
public:
MemoryIsNotDeallocated() : old_crtdbg_flag_(0) {
#ifdef _MSC_VER
old_crtdbg_flag_ = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
// Set heap allocation block type to _IGNORE_BLOCK
_CrtSetDbgFlag(old_crtdbg_flag_ & ~_CRTDBG_ALLOC_MEM_DF);
#endif // _MSC_VER
}

~MemoryIsNotDeallocated() {
#ifdef _MSC_VER
// Restore the original _CRTDBG_ALLOC_MEM_DF flag
_CrtSetDbgFlag(old_crtdbg_flag_);
#endif // _MSC_VER
}

private:
int old_crtdbg_flag_;

GTEST_DISALLOW_COPY_AND_ASSIGN_(MemoryIsNotDeallocated);
};

} // namespace

// Initializes owner_thread_id_ and critical_section_ in static mutexes.
void Mutex::ThreadSafeLazyInit() {
// Dynamic mutexes are initialized in the constructor.
Expand All @@ -289,7 +324,12 @@ void Mutex::ThreadSafeLazyInit() {
// 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;
{
// Flag that this mem alloc is never deallocated to avoid
// MS debug CRT reporting a false positive leak
MemoryIsNotDeallocated memory_is_not_deallocated;
critical_section_ = new CRITICAL_SECTION;
}
::InitializeCriticalSection(critical_section_);
// Updates the critical_section_init_phase_ to 2 to signal
// initialization complete.
Expand Down Expand Up @@ -528,10 +568,18 @@ class ThreadLocalRegistryImpl {
return 0;
}

// Return a newly constructed ThreadIdToThreadLocals that's intentionally never deleted
static ThreadIdToThreadLocals* NewThreadIdToThreadLocals() {
// Flag that this mem alloc is never deallocated to avoid
// MS debug CRT reporting a false positive leak
MemoryIsNotDeallocated memory_is_not_deallocated;
return new ThreadIdToThreadLocals;
}

// Returns map of thread local instances.
static ThreadIdToThreadLocals* GetThreadLocalsMapLocked() {
mutex_.AssertHeld();
static ThreadIdToThreadLocals* map = new ThreadIdToThreadLocals;
static ThreadIdToThreadLocals* map = NewThreadIdToThreadLocals();
return map;
}

Expand Down