Skip to content

Commit 2a30d70

Browse files
committed
use std::shared_ptr to much more easily implement the shared ownership semantics
1 parent a4ef6c6 commit 2a30d70

File tree

1 file changed

+25
-20
lines changed

1 file changed

+25
-20
lines changed

Firestore/core/src/util/thread_safe_memoizer.h

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -34,49 +34,54 @@ namespace util {
3434
template <typename T>
3535
class ThreadSafeMemoizer {
3636
public:
37-
ThreadSafeMemoizer() = default;
38-
39-
ThreadSafeMemoizer(const ThreadSafeMemoizer&) {
40-
}
41-
42-
ThreadSafeMemoizer& operator=(const ThreadSafeMemoizer&) {
43-
return *this;
37+
ThreadSafeMemoizer()
38+
: memoized_(new std::atomic<T*>(nullptr), MemoizedValueDeleter) {
4439
}
4540

41+
ThreadSafeMemoizer(const ThreadSafeMemoizer& other) = default;
42+
ThreadSafeMemoizer& operator=(const ThreadSafeMemoizer& other) = default;
4643
ThreadSafeMemoizer(ThreadSafeMemoizer&& other) = default;
4744
ThreadSafeMemoizer& operator=(ThreadSafeMemoizer&& other) = default;
4845

49-
~ThreadSafeMemoizer() {
50-
delete memoized_.load();
51-
}
52-
5346
/**
5447
* Memoize a value.
5548
*
56-
* If there is no memoized value then the given function is called to create
57-
* the value, returning a reference to the created value and storing the
58-
* pointer to the created value. On the other hand, if there _is_ a memoized
59-
* value from a previous invocation then a reference to that object is
60-
* returned and the given function is not called. Note that the given function
61-
* may be called more than once and, therefore, must be idempotent.
49+
* If there is _no_ memoized value then the given function is called to create
50+
* the object to memoize. The created object is then stored for use in future
51+
* invocations as the "memoized value". Finally, a reference to the created
52+
* object is returned.
53+
*
54+
* On the other hand, if there _is_ a memoized value, then a reference to that
55+
* memoized value object is returned and the given function is _not_ called.
56+
*
57+
* The given function *must* be idempotent because it _may_ be called more
58+
* than once due to the semantics of std::atomic::compare_exchange_weak().
59+
*
60+
* No reference to the given function is retained by this object, and the
61+
* function be called synchronously, if it is called at all.
6262
*/
6363
const T& memoize(std::function<std::unique_ptr<T>()> func) {
6464
while (true) {
65-
T* old_memoized = memoized_.load();
65+
T* old_memoized = memoized_->load();
6666
if (old_memoized) {
6767
return *old_memoized;
6868
}
6969

7070
std::unique_ptr<T> new_memoized = func();
7171

72-
if (memoized_.compare_exchange_strong(old_memoized, new_memoized.get())) {
72+
if (memoized_->compare_exchange_weak(old_memoized, new_memoized.get())) {
7373
return *new_memoized.release();
7474
}
7575
}
7676
}
7777

7878
private:
79-
std::atomic<T*> memoized_ = {nullptr};
79+
std::shared_ptr<std::atomic<T*>> memoized_;
80+
81+
static void MemoizedValueDeleter(std::atomic<T*>* value) {
82+
delete value->load();
83+
delete value;
84+
}
8085
};
8186

8287
} // namespace util

0 commit comments

Comments
 (0)