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

Firestore: Fix memory leak in Query.whereField() #14300

Open
wants to merge 104 commits into
base: main
Choose a base branch
from

Conversation

dconeybe
Copy link
Contributor

@dconeybe dconeybe commented Jan 3, 2025

This PR fixes an apparent memory leak that is detected by the "Xcode Leaks" tool. As of today, this leak has been reported at least twice: #13978 and #12613. I don't completely understand why this is flagged as a memory leak because, to me, I cannot find any "leaks"; however, I fixed the detected leak anyways. The investigation also uncovered an unrelated use-after-free bug that was fixed as well: #14306.

The leaks originated from std::shared_ptr<ThreadSafeMemoizer> usages, which manifested many public APIs, including Query.whereField(), queryWhereFieldPath:arrayContainsAny, and FIRQuery.addFields(using:).

Before this PR, all instances of ThreadSafeMemoizer were held in an std::shared_ptr so that the memoized value could be shared among copies of the object of which the std::shared_ptr is a member, and also to work around the limitation that ThreadSafeMemoizer itself was neither copyable nor moveable.

This PR completely rewrites ThreadSafeMemoizer<T> to make it both moveable and copyable, thus removing the need to store instances of it in a std::shared_ptr. The re-write has a single member variable of type std::shared_ptr<T> rather than a member variable of T directly. In order to make accesses to the std::shared_ptr<T> thread-safe, all access are done using the std::atomic_XXX() free functions, such as std::atomic_store(), std::atomic_load(), and std::atomic_compare_exchange_weak(). These std::atomic_XXX() free functions are deprecated in C++20, at which time the implementation should be changed to use std::atomic<std::shared_ptr<T>>, which is less error-prone.

The API of the re-written ThreadSafeMemoizer<T> also changes the const T& memoize(std::function<T()>) member function to const T& value(const std::function<std::shared_ptr<T>()>&); that is, it was renamed from "memoize" to "value" and the given function is expected to return std::shared_ptr<T> instead of just T. Doing this avoids unnecessarily copying or moving the T object into a newly-created std::shared_ptr<T> by ThreadSafeMemoizer<T>, thus supporting T objects that are not copyable and/or moveable.

The test suite for ThreadSafeMemoizer<T> was also vastly improved to test things like thread safety, copy and move operations, and invocation semantics. Various utility functions and classes were added into thread_safe_memoizer_testing.h (and implemented in the corresponding .cc file), with tests added for those utility classes and functions too.

Fixes: #13978

@dconeybe dconeybe self-assigned this Jan 3, 2025
@dconeybe dconeybe changed the base branch from main to dconeybe/StringFormatStringifySinkUseAfterFreeFix January 6, 2025 21:30
@dconeybe
Copy link
Contributor Author

dconeybe commented Jan 6, 2025

AI(dconeybe): Verify that the implementation that uses std::shared_ptr internally still fixes the memory leak.

Update: It does NOT fix the memory leak. I'm reverting back to the std::atomic implementation.

Base automatically changed from dconeybe/RemoveCpp11LintChecks to main January 13, 2025 22:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

memory leak issue while making a query calls with whereField(isEqualTo)
2 participants