37 enum class State { NoValue, Updating, HasValue };
40 std::atomic<State> state_{State::NoValue};
42 boost::signals2::signal<void(std::expected<ValueType, ErrorType>)> updateFinished_;
55 : state_{
State::HasValue}, value_(std::move(initialValue))
70 using Updater = std::function<std::expected<ValueType, ErrorType>(boost::asio::yield_context)>;
76 using Verifier = std::function<bool(ValueType
const&)>;
91 [[nodiscard]] std::expected<ValueType, ErrorType>
95 case State::Updating: {
96 return wait(yield, std::move(updater), std::move(verifier));
98 case State::HasValue: {
99 auto const value = value_.template lock<std::shared_lock>();
100 ASSERT(value->has_value(),
"Value should be presented when the cache is full");
101 return value->value();
103 case State::NoValue: {
104 return update(yield, std::move(updater), std::move(verifier));
121 [[nodiscard]] std::expected<ValueType, ErrorType>
124 if (state_ == State::Updating) {
125 return asyncGet(yield, std::move(updater), std::move(verifier));
127 state_ = State::Updating;
129 auto const result = updater(yield);
130 auto const shouldBeCached = result.has_value() and verifier(result.value());
132 if (shouldBeCached) {
133 value_.lock().get() = result.value();
134 state_ = State::HasValue;
136 state_ = State::NoValue;
137 value_.lock().get() = std::nullopt;
140 updateFinished_(result);
153 if (state_ == State::HasValue) {
154 state_ = State::NoValue;
155 value_.lock().get() = std::nullopt;
180 std::expected<ValueType, ErrorType>
181 wait(boost::asio::yield_context yield, Updater updater, Verifier verifier)
183 struct SharedContext {
184 SharedContext(boost::asio::yield_context y)
185 : timer(y.get_executor(), boost::asio::steady_timer::duration::max())
189 boost::asio::steady_timer timer;
190 std::optional<std::expected<ValueType, ErrorType>> result;
193 auto sharedContext = std::make_shared<SharedContext>(yield);
194 boost::system::error_code errorCode;
196 boost::signals2::scoped_connection
const slot =
197 updateFinished_.connect([yield,
198 sharedContext](std::expected<ValueType, ErrorType> value) {
201 [sharedContext = std::move(sharedContext), value = std::move(value)](
auto&&) {
202 sharedContext->result = std::move(value);
203 sharedContext->timer.cancel();
208 if (state_ == State::Updating) {
209 sharedContext->timer.async_wait(yield[errorCode]);
210 ASSERT(sharedContext->result.has_value(),
"There should be some value after waiting");
211 return std::move(sharedContext->result).value();
213 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:92
std::function< std::expected< ValueType, ErrorType >(boost::asio::yield_context)> Updater
Function type for cache update operations.
Definition BlockingCache.hpp:70
std::expected< ValueType, ErrorType > update(boost::asio::yield_context yield, Updater updater, Verifier verifier)
Force an update of the cache value.
Definition BlockingCache.hpp:122
std::function< bool(ValueType const &)> Verifier
Function type to verify if a value should be cached.
Definition BlockingCache.hpp:76