@@ -34,49 +34,54 @@ namespace util {
3434template <typename T>
3535class 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