| 200 | } |
| 201 | |
| 202 | void LocalExchangeQueue::noMoreData() { |
| 203 | auto abort = aborted_.rlock(); |
| 204 | if (*abort) { |
| 205 | return; |
| 206 | } |
| 207 | |
| 208 | std::vector<ContinuePromise> consumerPromises; |
| 209 | queue_.withWLock([&](auto& queue) { |
| 210 | BOLT_CHECK_GT(pendingProducers_, 0); |
| 211 | --pendingProducers_; |
| 212 | if (noMoreProducers_ && pendingProducers_ == 0) { |
| 213 | // [morsel-driven] optionally notify the PartitionedOutputBuffer of the |
| 214 | // task one creator is finished |
| 215 | // Start the last driver using all remaining data chunks in the queue, |
| 216 | // even if the queue is empty because if the last morsel-driven driver |
| 217 | // finishes before the last driver creator finishes, the query might hang |
| 218 | // because Task::allPeersFinished will return false and since there is no |
| 219 | // more new driver, then the HashTable will never finish. |
| 220 | if (driverDispatcher_) { |
| 221 | uint64_t reducedBytes = 0; |
| 222 | std::shared_ptr<LocalExchangeQueue> primedQueue = |
| 223 | spawnPrimedQueueLocked(queue, reducedBytes); |
| 224 | if (driverDispatcher_->schedule(primedQueue, partition_, true)) { |
| 225 | memoryManager_->decreaseMemoryUsage(reducedBytes); |
| 226 | } else { |
| 227 | VLOG(1) << "Dispatcher already finished. No new driver created."; |
| 228 | } |
| 229 | } |
| 230 | |
| 231 | consumerPromises = std::move(consumerPromises_); |
| 232 | } |
| 233 | }); |
| 234 | notify(consumerPromises); |
| 235 | } |
| 236 | |
| 237 | BlockingReason LocalExchangeQueue::next( |
| 238 | ContinueFuture* future, |
no test coverage detected