| 175 | } |
| 176 | |
| 177 | void OneTaskDone() { |
| 178 | // Can be called unlocked thanks to atomics |
| 179 | auto nremaining = nremaining_.fetch_sub(1, std::memory_order_release) - 1; |
| 180 | DCHECK_GE(nremaining, 0); |
| 181 | if (nremaining == 0) { |
| 182 | // Take the lock so that ~ThreadedTaskGroup cannot destroy cv |
| 183 | // before cv.notify_one() has returned |
| 184 | std::unique_lock<std::mutex> lock(mutex_); |
| 185 | cv_.notify_one(); |
| 186 | if (completion_future_.has_value()) { |
| 187 | // MarkFinished could be slow. We don't want to call it while we are holding |
| 188 | // the lock. |
| 189 | auto& future = *completion_future_; |
| 190 | const auto finished = completion_future_->is_finished(); |
| 191 | const auto& status = status_; |
| 192 | // This will be redundant if the user calls Finish and not FinishAsync |
| 193 | if (!finished && !finished_) { |
| 194 | finished_ = true; |
| 195 | lock.unlock(); |
| 196 | future.MarkFinished(status); |
| 197 | } else { |
| 198 | lock.unlock(); |
| 199 | } |
| 200 | } |
| 201 | } |
| 202 | } |
| 203 | |
| 204 | // These members are usable unlocked |
| 205 | Executor* executor_; |
no test coverage detected