|
2 | 2 |
|
3 | 3 | #include <launchdarkly/async/timer.hpp> |
4 | 4 |
|
| 5 | +#include <algorithm> |
5 | 6 | #include <utility> |
6 | 7 | #include <variant> |
7 | 8 |
|
@@ -133,15 +134,73 @@ async::Future<IFDv2Condition::Type> MakeAggregateFuture( |
133 | 134 | } // namespace |
134 | 135 |
|
135 | 136 | Conditions::Conditions(std::vector<std::unique_ptr<IFDv2Condition>> conditions) |
136 | | - : conditions_(std::move(conditions)), |
137 | | - future_(MakeAggregateFuture(conditions_)) {} |
| 137 | + : conditions_(std::move(conditions)), state_(std::make_shared<State>()) { |
| 138 | + MakeAggregateFuture(conditions_) |
| 139 | + .Then( |
| 140 | + [state = |
| 141 | + state_](IFDv2Condition::Type const& type) -> std::monostate { |
| 142 | + std::vector<PendingEntry> drained; |
| 143 | + { |
| 144 | + std::lock_guard lock(state->mutex); |
| 145 | + state->aggregate_result = type; |
| 146 | + drained = std::move(state->pending); |
| 147 | + } |
| 148 | + for (auto& entry : drained) { |
| 149 | + entry.promise.Resolve(type); |
| 150 | + } |
| 151 | + return {}; |
| 152 | + }, |
| 153 | + async::kInlineExecutor); |
| 154 | +} |
138 | 155 |
|
139 | 156 | Conditions::~Conditions() { |
140 | 157 | Close(); |
141 | 158 | } |
142 | 159 |
|
143 | | -async::Future<IFDv2Condition::Type> Conditions::GetFuture() const { |
144 | | - return future_; |
| 160 | +async::Future<IFDv2Condition::Type> Conditions::GetFuture( |
| 161 | + async::CancellationToken token) { |
| 162 | + async::Promise<IFDv2Condition::Type> promise; |
| 163 | + auto future = promise.GetFuture(); |
| 164 | + std::int64_t id; |
| 165 | + |
| 166 | + { |
| 167 | + std::lock_guard lock(state_->mutex); |
| 168 | + if (state_->aggregate_result) { |
| 169 | + promise.Resolve(*state_->aggregate_result); |
| 170 | + return future; |
| 171 | + } |
| 172 | + id = state_->next_id++; |
| 173 | + state_->pending.push_back({id, std::move(promise), nullptr}); |
| 174 | + } |
| 175 | + |
| 176 | + // Construct cb outside the lock: if the token is already cancelled, the |
| 177 | + // callback fires synchronously inside the ctor and needs to acquire |
| 178 | + // state_->mutex. |
| 179 | + auto cb = std::make_unique<async::CancellationCallback>( |
| 180 | + token, [state = state_, id]() { |
| 181 | + std::lock_guard lock(state->mutex); |
| 182 | + state->pending.erase( |
| 183 | + std::remove_if( |
| 184 | + state->pending.begin(), state->pending.end(), |
| 185 | + [id](PendingEntry const& e) { return e.id == id; }), |
| 186 | + state->pending.end()); |
| 187 | + }); |
| 188 | + |
| 189 | + // Re-find the entry under the lock and attach cb. Between the push above |
| 190 | + // and here, the aggregate may have fired and drained the vector, or cb's |
| 191 | + // callback may have fired synchronously during construction and erased |
| 192 | + // the entry. If gone, drop cb. |
| 193 | + { |
| 194 | + std::lock_guard lock(state_->mutex); |
| 195 | + auto it = |
| 196 | + std::find_if(state_->pending.begin(), state_->pending.end(), |
| 197 | + [id](PendingEntry const& e) { return e.id == id; }); |
| 198 | + if (it != state_->pending.end()) { |
| 199 | + it->cancel_cb = std::move(cb); |
| 200 | + } |
| 201 | + } |
| 202 | + |
| 203 | + return future; |
145 | 204 | } |
146 | 205 |
|
147 | 206 | void Conditions::Inform(FDv2SourceResult const& result) { |
|
0 commit comments