56 enum class State { NoValue, Updating, HasValue };
59 std::atomic<State> state_{State::NoValue};
61 boost::signals2::signal<void(std::expected<ValueType, ErrorType>)> updateFinished_;
74 : state_{
State::HasValue}, value_(std::move(initialValue))
89 using Updater = std::function<std::expected<ValueType, ErrorType>(boost::asio::yield_context)>;
95 using Verifier = std::function<bool(ValueType
const&)>;
110 [[nodiscard]] std::expected<ValueType, ErrorType>
114 case State::Updating: {
115 return wait(yield, std::move(updater), std::move(verifier));
117 case State::HasValue: {
118 auto const value = value_.template lock<std::shared_lock>();
119 ASSERT(value->has_value(),
"Value should be presented when the cache is full");
120 return value->value();
122 case State::NoValue: {
123 return update(yield, std::move(updater), std::move(verifier));
140 [[nodiscard]] std::expected<ValueType, ErrorType>
143 if (state_ == State::Updating) {
144 return asyncGet(yield, std::move(updater), std::move(verifier));
146 state_ = State::Updating;
148 auto const result = updater(yield);
149 auto const shouldBeCached = result.has_value() and verifier(result.value());
151 if (shouldBeCached) {
152 value_.lock().get() = result.value();
153 state_ = State::HasValue;
155 state_ = State::NoValue;
156 value_.lock().get() = std::nullopt;
159 updateFinished_(result);
172 if (state_ == State::HasValue) {
173 state_ = State::NoValue;
174 value_.lock().get() = std::nullopt;
199 std::expected<ValueType, ErrorType>
200 wait(boost::asio::yield_context yield, Updater updater, Verifier verifier)
202 struct SharedContext {
203 SharedContext(boost::asio::yield_context y)
204 : timer(y.get_executor(), boost::asio::steady_timer::duration::max())
208 boost::asio::steady_timer timer;
209 std::optional<std::expected<ValueType, ErrorType>> result;
212 auto sharedContext = std::make_shared<SharedContext>(yield);
213 boost::system::error_code errorCode;
215 boost::signals2::scoped_connection
const slot =
216 updateFinished_.connect([yield,
217 sharedContext](std::expected<ValueType, ErrorType> value) {
220 [sharedContext = std::move(sharedContext), value = std::move(value)](
auto&&) {
221 sharedContext->result = std::move(value);
222 sharedContext->timer.cancel();
227 if (state_ == State::Updating) {
228 sharedContext->timer.async_wait(yield[errorCode]);
229 ASSERT(sharedContext->result.has_value(),
"There should be some value after waiting");
230 return std::move(sharedContext->result).value();
232 return asyncGet(yield, std::move(updater), std::move(verifier));
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:111
std::function< std::expected< ValueType, ErrorType >(boost::asio::yield_context)> Updater
Function type for cache update operations.
Definition BlockingCache.hpp:89
std::expected< ValueType, ErrorType > update(boost::asio::yield_context yield, Updater updater, Verifier verifier)
Force an update of the cache value.
Definition BlockingCache.hpp:141
std::function< bool(ValueType const &)> Verifier
Function type to verify if a value should be cached.
Definition BlockingCache.hpp:95