diff options
| author | Mikaël Capelle <capelle.mikael@gmail.com> | 2020-05-24 23:32:47 +0200 |
|---|---|---|
| committer | Mikaël Capelle <capelle.mikael@gmail.com> | 2020-05-24 23:32:47 +0200 |
| commit | 0aac4f3291d65ebc1b0bbee744fca347ecef7022 (patch) | |
| tree | ec3ca88e1971a6ec380ebdc7301f9931b9a7d7fa | |
| parent | b352aa53e0005cc262ab7441bd374570d6423279 (diff) | |
Add MemoizedLocked.
| -rw-r--r-- | src/thread_utils.h | 43 |
1 files changed, 43 insertions, 0 deletions
diff --git a/src/thread_utils.h b/src/thread_utils.h index 2cb87a8d..6c044138 100644 --- a/src/thread_utils.h +++ b/src/thread_utils.h @@ -7,6 +7,49 @@ namespace MOShared { /** + * Class that can be used to perform thread-safe memoization. + * + * Each instance hold a flag indicating if the current value is up-to-date + * or not. This flag can be reset using `invalidate()`. When the value is queried, + * the flag is checked, and if it is not up-to-date, the given callback is used + * to compute the value. + * + * The computation and update of the value is locked to avoid concurrent modifications. + * + * @tparam T Type of value ot memoized. + * @tparam Fn Type of the callback. + */ +template <class T, class Fn> +struct MemoizedLocked { + + MemoizedLocked(Fn callback, T value = {}) : + m_Fn{ callback }, m_Value{ std::move(value) } { } + + template <class... Args> + T& value(Args&&... args) const { + if (m_NeedUpdating) { + std::scoped_lock lock(m_Mutex); + if (m_NeedUpdating) { + m_Value = std::invoke(m_Fn, std::forward<Args>(args)... ); + m_NeedUpdating.store(false); + } + } + return m_Value; + } + + void invalidate() const { + m_NeedUpdating.store(true); + } + +private: + mutable std::mutex m_Mutex; + mutable std::atomic<bool> m_NeedUpdating{ true }; + + Fn m_Fn; + mutable T m_Value; +}; + +/** * @brief Apply the given callable to each element between the two given iterators * in a parallel way. * |
