22#include "util/Assert.hpp"
23#include "util/Mutex.hpp"
25#include <boost/asio/error.hpp>
26#include <boost/asio/spawn.hpp>
27#include <boost/asio/steady_timer.hpp>
28#include <boost/signals2/connection.hpp>
29#include <boost/signals2/signal.hpp>
30#include <boost/signals2/variadic_signal.hpp>
37#include <shared_mutex>
48template <
typename ValueType,
typename ErrorType>
49 requires(not std::same_as<ValueType, ErrorType>)
55 enum class State { NoValue, Updating, HasValue };
58 std::atomic<State> state_{State::NoValue};
60 boost::signals2::signal<void(std::expected<ValueType, ErrorType>)> updateFinished_;
72 explicit BlockingCache(ValueType initialValue) : state_{
State::HasValue}, value_(std::move(initialValue))
87 using Updater = std::function<std::expected<ValueType, ErrorType>(boost::asio::yield_context)>;
93 using Verifier = std::function<bool(ValueType
const&)>;
108 [[nodiscard]] std::expected<ValueType, ErrorType>
112 case State::Updating: {
113 return wait(yield, std::move(updater), std::move(verifier));
115 case State::HasValue: {
116 auto const value = value_.template lock<std::shared_lock>();
117 ASSERT(value->has_value(),
"Value should be presented when the cache is full");
118 return value->value();
120 case State::NoValue: {
121 return update(yield, std::move(updater), std::move(verifier));
138 [[nodiscard]] std::expected<ValueType, ErrorType>
141 if (state_ == State::Updating) {
142 return asyncGet(yield, std::move(updater), std::move(verifier));
144 state_ = State::Updating;
146 auto const result = updater(yield);
147 auto const shouldBeCached = result.has_value() and verifier(result.value());
149 if (shouldBeCached) {
150 value_.
lock().get() = result.value();
151 state_ = State::HasValue;
153 state_ = State::NoValue;
154 value_.
lock().get() = std::nullopt;
157 updateFinished_(result);
170 if (state_ == State::HasValue) {
171 state_ = State::NoValue;
172 value_.
lock().get() = std::nullopt;
197 std::expected<ValueType, ErrorType>
198 wait(boost::asio::yield_context yield, Updater updater, Verifier verifier)
200 boost::asio::steady_timer timer{yield.get_executor(), boost::asio::steady_timer::duration::max()};
201 boost::system::error_code errorCode;
203 std::optional<std::expected<ValueType, ErrorType>> result;
204 boost::signals2::scoped_connection slot =
205 updateFinished_.connect([yield, &timer, &result](std::expected<ValueType, ErrorType> value) {
206 boost::asio::spawn(yield, [&timer, &result, value = std::move(value)](
auto&&) {
207 result = std::move(value);
212 if (state_ == State::Updating) {
213 timer.async_wait(yield[errorCode]);
214 ASSERT(result.has_value(),
"There should be some value after waiting");
215 return std::move(result).value();
217 return asyncGet(yield, std::move(updater), std::move(verifier));
A thread-safe cache that blocks getting operations until the cache is updated.
Definition BlockingCache.hpp:50
void invalidate()
Invalidates the currently cached value if present.
Definition BlockingCache.hpp:168
BlockingCache(ValueType initialValue)
Construct a cache with an initial value.
Definition BlockingCache.hpp:72
std::expected< ValueType, ErrorType > asyncGet(boost::asio::yield_context yield, Updater updater, Verifier verifier)
Asynchronously get a value from the cache, updating if necessary.
Definition BlockingCache.hpp:109
State state() const
Returns the current state of the cache.
Definition BlockingCache.hpp:181
State
Possible states of the cache.
Definition BlockingCache.hpp:55
std::function< std::expected< ValueType, ErrorType >(boost::asio::yield_context)> Updater
Function type for cache update operations.
Definition BlockingCache.hpp:87
BlockingCache()=default
Default constructor - creates an empty cache.
std::expected< ValueType, ErrorType > update(boost::asio::yield_context yield, Updater updater, Verifier verifier)
Force an update of the cache value.
Definition BlockingCache.hpp:139
std::function< bool(ValueType const &)> Verifier
Function type to verify if a value should be cached.
Definition BlockingCache.hpp:93
A container for data that is protected by a mutex. Inspired by Mutex in Rust.
Definition Mutex.hpp:96
Lock< ProtectedDataType const, LockType, MutexType > lock() const
Lock the mutex and get a lock object allowing access to the protected data.
Definition Mutex.hpp:134
This namespace contains various utilities.
Definition AccountUtils.hpp:30