| 1274 | : state(std::move(state)), index(index), recursive(recursive) {} |
| 1275 | |
| 1276 | void operator()(const Result<T>& maybe_next_ref) { |
| 1277 | // An item has been delivered by one of the inner subscriptions |
| 1278 | Future<T> next_fut; |
| 1279 | const Result<T>* maybe_next = &maybe_next_ref; |
| 1280 | |
| 1281 | // When an item is delivered (and the caller has asked for it) we grab the |
| 1282 | // next item from the inner subscription. To avoid this behavior leading to an |
| 1283 | // infinite loop (this can happen if the caller's callback asks for the next item) |
| 1284 | // we use a while loop. |
| 1285 | while (true) { |
| 1286 | Future<T> sink; |
| 1287 | bool sub_finished = maybe_next->ok() && IsIterationEnd(**maybe_next); |
| 1288 | bool pull_next_sub = false; |
| 1289 | bool was_broken = false; |
| 1290 | bool should_mark_gen_complete = false; |
| 1291 | bool should_mark_final_error = false; |
| 1292 | { |
| 1293 | auto guard = state->mutex.Lock(); |
| 1294 | if (state->broken) { |
| 1295 | // We've errored out previously so ignore the result. If anyone was waiting |
| 1296 | // for this they will get IterationEnd when we purge |
| 1297 | was_broken = true; |
| 1298 | } else { |
| 1299 | if (!sub_finished) { |
| 1300 | // There is a result to deliver. Either we can deliver it now or we will |
| 1301 | // queue it up |
| 1302 | if (state->waiting_jobs.empty()) { |
| 1303 | state->delivered_jobs.push_back(std::make_shared<DeliveredJob>( |
| 1304 | state->active_subscriptions[index], *maybe_next, index)); |
| 1305 | } else { |
| 1306 | sink = std::move(*state->waiting_jobs.front()); |
| 1307 | state->waiting_jobs.pop_front(); |
| 1308 | } |
| 1309 | } |
| 1310 | |
| 1311 | // If this is the first error then we transition the state to a broken state |
| 1312 | if (!maybe_next->ok()) { |
| 1313 | should_mark_final_error = true; |
| 1314 | state->SignalErrorUnlocked(guard); |
| 1315 | } |
| 1316 | } |
| 1317 | |
| 1318 | // If we finished this inner subscription then we need to grab a new inner |
| 1319 | // subscription to take its spot. If we can't (because we're broken or |
| 1320 | // exhausted) then we aren't going to be starting any new futures and so |
| 1321 | // the number of running subscriptions drops. |
| 1322 | pull_next_sub = sub_finished && !state->source_exhausted && !was_broken; |
| 1323 | if (sub_finished && !pull_next_sub) { |
| 1324 | state->num_running_subscriptions--; |
| 1325 | } |
| 1326 | // There are three situations we won't pull again. If an error occurred or we |
| 1327 | // are already finished or if no one was waiting for our result and so we queued |
| 1328 | // it up. We will decrement outstanding_requests and possibly mark the |
| 1329 | // generator completed. |
| 1330 | if (state->broken || (!sink.is_valid() && !sub_finished) || |
| 1331 | (sub_finished && state->source_exhausted)) { |
| 1332 | if (state->MarkTaskFinishedUnlocked(guard)) { |
| 1333 | should_mark_gen_complete = true; |
nothing calls this directly
no test coverage detected