41 : _inner{&inner}, _state{std::make_shared<State>(owner)} {}
48 [[nodiscard]] std::shared_ptr<ModelChannel>
bind(std::string
const& typeId, bridge::BindSharing sharing,
49 std::string
const& instanceKey)
override {
50 return std::make_shared<Channel>(_inner->bind(typeId, sharing, instanceKey), _state);
56 void replay() { State::next(_state); }
60 [[nodiscard]] std::size_t
held() const noexcept {
return _state->held.size(); }
64 void setOnHeld(std::function<
void(std::size_t)> observer) { _state->observer = std::move(observer); }
68 std::shared_ptr<ModelChannel> channel;
77 exec::IExecutor* owner;
78 std::deque<Held>
held;
79 bool replaying =
false;
80 std::function<void(std::size_t)> observer;
83 void hold(Held call) {
84 held.push_back(std::move(call));
88 void changed()
const {
90 observer(held.size());
95 static void next(std::shared_ptr<State>
const& state) {
96 if (state->replaying || state->held.empty()) {
99 state->replaying =
true;
100 Held
const& front = state->held.front();
101 std::weak_ptr<State>
const weak = state;
102 front.channel->execute(front.actionId, front.bodyJson, CallOptions{})
103 .then([weak](std::string
const& reply) {
104 if (
auto const held = weak.lock()) {
105 settle(held, [&reply](Held& call) { call.promise.resolve(reply); });
108 .onError([weak](std::exception_ptr
const& error) {
109 auto const held = weak.lock();
113 if (isOffline(error)) {
115 held->replaying =
false;
118 settle(held, [&error](Held& call) { call.promise.reject(error); });
123 static void settle(std::shared_ptr<State>
const& state, std::function<
void(Held&)>
const& outcome) {
124 Held call = std::move(state->held.front());
125 state->held.pop_front();
126 state->replaying =
false;
133 class Channel final :
public ModelChannel {
135 Channel(std::shared_ptr<ModelChannel> inner, std::shared_ptr<State> state)
136 : _inner{std::move(inner)}, _state{std::move(state)} {}
138 [[nodiscard]] async::Completion<std::string> execute(std::string
const& actionId, std::string
const& bodyJson,
139 CallOptions options)
override {
140 if (!options.queueOffline || options.evaluate) {
141 return _inner->execute(actionId, bodyJson, options);
144 if (!_state->held.empty()) {
147 .channel = _inner, .actionId = actionId, .bodyJson = bodyJson, .promise = std::move(promise)});
148 return std::move(
held);
150 auto const settle = std::make_shared<async::Completion<std::string>::Promise>(std::move(promise));
151 std::weak_ptr<State>
const weak = _state;
152 _inner->execute(actionId, bodyJson, options)
153 .then([settle](std::string
const& reply) { settle->resolve(reply); })
154 .onError([inner = _inner, weak, settle, actionId, bodyJson](std::exception_ptr
const& error) {
155 auto const state = weak.lock();
156 if (state && isOffline(error)) {
157 state->hold(Held{.channel = inner,
158 .actionId = actionId,
159 .bodyJson = bodyJson,
160 .promise = std::move(*settle)});
163 settle->reject(error);
165 return std::move(
held);
169 std::shared_ptr<ModelChannel> _inner;
170 std::shared_ptr<State> _state;
173 [[nodiscard]]
static bool isOffline(std::exception_ptr
const& error) {
175 std::rethrow_exception(error);
176 }
catch (backend::DisconnectedError
const&) {
184 std::shared_ptr<State> _state;