1818#define FIRESTORE_CORE_SRC_UTIL_THREAD_SAFE_MEMOIZER_H_
1919
2020#include < functional>
21- #include < mutex> // NOLINT(build/c++11)
22- #include < vector>
21+ #include < memory>
2322
2423namespace firebase {
2524namespace firestore {
@@ -36,43 +35,47 @@ class ThreadSafeMemoizer {
3635 public:
3736 ThreadSafeMemoizer () = default ;
3837
39- ~ThreadSafeMemoizer () {
40- // Call `std::call_once` in order to synchronize with the "active"
41- // invocation of `memoize()`. Without this synchronization, there is a data
42- // race between this destructor, which "reads" `memoized_value_` to destroy
43- // it, and the write to `memoized_value_` done by the "active" invocation of
44- // `memoize()`.
45- std::call_once (once_, [&]() {});
38+ ThreadSafeMemoizer (const ThreadSafeMemoizer&) {
39+ }
40+
41+ ThreadSafeMemoizer& operator =(const ThreadSafeMemoizer&) {
42+ return *this ;
4643 }
4744
48- // This class cannot be copied or moved, because it has `std::once_flag`
49- // member.
50- ThreadSafeMemoizer ( const ThreadSafeMemoizer&) = delete ;
51- ThreadSafeMemoizer (ThreadSafeMemoizer&&) = delete ;
52- ThreadSafeMemoizer& operator =( const ThreadSafeMemoizer&) = delete ;
53- ThreadSafeMemoizer& operator =(ThreadSafeMemoizer&&) = delete ;
45+ ThreadSafeMemoizer (ThreadSafeMemoizer&& other) = default ;
46+ ThreadSafeMemoizer& operator =(ThreadSafeMemoizer&& other) = default ;
47+
48+ ~ ThreadSafeMemoizer () {
49+ delete memoized_. load () ;
50+ }
5451
5552 /* *
5653 * Memoize a value.
5754 *
58- * The std::function object specified by the first invocation of this
59- * function (the "active" invocation) will be invoked synchronously.
60- * None of the std::function objects specified by the subsequent
61- * invocations of this function (the "passive" invocations) will be
62- * invoked. All invocations, both "active" and "passive", will return a
63- * reference to the std::vector created by copying the return value from
64- * the std::function specified by the "active" invocation. It is,
65- * therefore, the "active" invocation's job to return the std::vector
66- * to memoize.
55+ * If there is no memoized value then the given function is called to create
56+ * the value, returning a reference to the created value and storing the
57+ * pointer to the created value. On the other hand, if there _is_ a memoized
58+ * value from a previous invocation then a reference to that object is
59+ * returned and the given function is not called. Note that the given function
60+ * may be called more than once and, therefore, must be idempotent.
6761 */
68- const T& memoize (std::function<T()> func) {
69- std::call_once (once_, [&]() { memoized_value_ = func (); });
70- return memoized_value_;
62+ const T& memoize (std::function<std::unique_ptr<T>()> func) {
63+ while (true ) {
64+ T* old_memoized = memoized_.load ();
65+ if (old_memoized) {
66+ return *old_memoized;
67+ }
68+
69+ std::unique_ptr<T> new_memoized = func ();
70+
71+ if (memoized_.compare_exchange_strong (old_memoized, new_memoized.get ())) {
72+ return *new_memoized.release ();
73+ }
74+ }
7175 }
7276
7377 private:
74- std::once_flag once_;
75- T memoized_value_;
78+ std::atomic<T*> memoized_ = {nullptr };
7679};
7780
7881} // namespace util
0 commit comments