The worker loop is an independent function so that it can keep running after the ThreadPool is destroyed.
| 446 | // The worker loop is an independent function so that it can keep running |
| 447 | // after the ThreadPool is destroyed. |
| 448 | static void WorkerLoop(std::shared_ptr<ThreadPool::State> state, |
| 449 | std::list<std::thread>::iterator it) { |
| 450 | std::unique_lock<std::mutex> lock(state->mutex_); |
| 451 | |
| 452 | // Since we hold the lock, `it` now points to the correct thread object |
| 453 | // (LaunchWorkersUnlocked has exited) |
| 454 | DCHECK_EQ(std::this_thread::get_id(), it->get_id()); |
| 455 | |
| 456 | // If too many threads, we should secede from the pool |
| 457 | const auto should_secede = [&]() -> bool { |
| 458 | return state->workers_.size() > static_cast<size_t>(state->desired_capacity_); |
| 459 | }; |
| 460 | |
| 461 | while (true) { |
| 462 | // By the time this thread is started, some tasks may have been pushed |
| 463 | // or shutdown could even have been requested. So we only wait on the |
| 464 | // condition variable at the end of the loop. |
| 465 | |
| 466 | // Execute pending tasks if any |
| 467 | while (!state->pending_tasks_.empty() && !state->quick_shutdown_) { |
| 468 | // We check this opportunistically at each loop iteration since |
| 469 | // it releases the lock below. |
| 470 | if (should_secede()) { |
| 471 | break; |
| 472 | } |
| 473 | |
| 474 | DCHECK_GE(state->tasks_queued_or_running_, 0); |
| 475 | { |
| 476 | Task task = std::move(const_cast<Task&>(state->pending_tasks_.top().task)); |
| 477 | state->pending_tasks_.pop(); |
| 478 | StopToken* stop_token = &task.stop_token; |
| 479 | lock.unlock(); |
| 480 | if (!stop_token->IsStopRequested()) { |
| 481 | std::move(task.callable)(); |
| 482 | } else { |
| 483 | if (task.stop_callback) { |
| 484 | std::move(task.stop_callback)(stop_token->Poll()); |
| 485 | } |
| 486 | } |
| 487 | { |
| 488 | auto tmp_task = std::move(task); // release resources before waiting for lock |
| 489 | ARROW_UNUSED(tmp_task); |
| 490 | } |
| 491 | lock.lock(); |
| 492 | } |
| 493 | if (ARROW_PREDICT_FALSE(--state->tasks_queued_or_running_ == 0)) { |
| 494 | state->cv_idle_.notify_all(); |
| 495 | } |
| 496 | } |
| 497 | // Now either the queue is empty *or* a quick shutdown was requested |
| 498 | if (state->please_shutdown_ || should_secede()) { |
| 499 | break; |
| 500 | } |
| 501 | // Wait for next wakeup |
| 502 | state->cv_.wait(lock); |
| 503 | } |
| 504 | DCHECK_GE(state->tasks_queued_or_running_, 0); |
| 505 |
no test coverage detected