22#include "util/Assert.hpp"
23#include "util/Mutex.hpp"
24#include "util/Spawn.hpp"
26#include <boost/asio/error.hpp>
27#include <boost/asio/spawn.hpp>
28#include <boost/asio/steady_timer.hpp>
29#include <boost/signals2/connection.hpp>
30#include <boost/signals2/signal.hpp>
31#include <boost/signals2/variadic_signal.hpp>
38#include <shared_mutex>
49template <
typename ValueType,
typename ErrorType>
50 requires(not std::same_as<ValueType, ErrorType>)
56 enum class State { NoValue, Updating, HasValue };
59 std::atomic<State> state_{State::NoValue};
61 boost::signals2::signal<void(std::expected<ValueType, ErrorType>)> updateFinished_;
73 explicit BlockingCache(ValueType initialValue) : state_{
State::HasValue}, value_(std::move(initialValue))
88 using Updater = std::function<std::expected<ValueType, ErrorType>(boost::asio::yield_context)>;
94 using Verifier = std::function<bool(ValueType
const&)>;
109 [[nodiscard]] std::expected<ValueType, ErrorType>
113 case State::Updating: {
114 return wait(yield, std::move(updater), std::move(verifier));
116 case State::HasValue: {
117 auto const value = value_.template lock<std::shared_lock>();
118 ASSERT(value->has_value(),
"Value should be presented when the cache is full");
119 return value->value();
121 case State::NoValue: {
122 return update(yield, std::move(updater), std::move(verifier));
139 [[nodiscard]] std::expected<ValueType, ErrorType>
142 if (state_ == State::Updating) {
143 return asyncGet(yield, std::move(updater), std::move(verifier));
145 state_ = State::Updating;
147 auto const result = updater(yield);
148 auto const shouldBeCached = result.has_value() and verifier(result.value());
150 if (shouldBeCached) {
151 value_.
lock().get() = result.value();
152 state_ = State::HasValue;
154 state_ = State::NoValue;
155 value_.
lock().get() = std::nullopt;
158 updateFinished_(result);
171 if (state_ == State::HasValue) {
172 state_ = State::NoValue;
173 value_.
lock().get() = std::nullopt;
198 std::expected<ValueType, ErrorType>
199 wait(boost::asio::yield_context yield, Updater updater, Verifier verifier)
201 struct SharedContext {
202 SharedContext(boost::asio::yield_context y)
203 : timer(y.get_executor(), boost::asio::steady_timer::duration::max())
207 boost::asio::steady_timer timer;
208 std::optional<std::expected<ValueType, ErrorType>> result;
211 auto sharedContext = std::make_shared<SharedContext>(yield);
212 boost::system::error_code errorCode;
214 boost::signals2::scoped_connection
const slot =
215 updateFinished_.connect([yield, sharedContext](std::expected<ValueType, ErrorType> value) {
216 util::spawn(yield, [sharedContext = std::move(sharedContext), value = std::move(value)](
auto&&) {
217 sharedContext->result = std::move(value);
218 sharedContext->timer.cancel();
222 if (state_ == State::Updating) {
223 sharedContext->timer.async_wait(yield[errorCode]);
224 ASSERT(sharedContext->result.has_value(),
"There should be some value after waiting");
225 return std::move(sharedContext->result).value();
227 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:51
void invalidate()
Invalidates the currently cached value if present.
Definition BlockingCache.hpp:169
BlockingCache(ValueType initialValue)
Construct a cache with an initial value.
Definition BlockingCache.hpp:73
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:110
State state() const
Returns the current state of the cache.
Definition BlockingCache.hpp:182
State
Possible states of the cache.
Definition BlockingCache.hpp:56
std::function< std::expected< ValueType, ErrorType >(boost::asio::yield_context)> Updater
Function type for cache update operations.
Definition BlockingCache.hpp:88
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:140
std::function< bool(ValueType const &)> Verifier
Function type to verify if a value should be cached.
Definition BlockingCache.hpp:94
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
void spawn(Ctx &&ctx, F &&func)
Spawns a coroutine using boost::asio::spawn
Definition Spawn.hpp:69