| 589 | }; |
| 590 | |
| 591 | class ThreadPool : public TaskQueue { |
| 592 | public: |
| 593 | explicit ThreadPool(size_t n) : shutdown_(false) { |
| 594 | while (n) { |
| 595 | threads_.emplace_back(worker(*this)); |
| 596 | n--; |
| 597 | } |
| 598 | } |
| 599 | |
| 600 | ThreadPool(const ThreadPool &) = delete; |
| 601 | ~ThreadPool() override = default; |
| 602 | |
| 603 | void enqueue(std::function<void()> fn) override { |
| 604 | { |
| 605 | std::unique_lock<std::mutex> lock(mutex_); |
| 606 | jobs_.push_back(std::move(fn)); |
| 607 | } |
| 608 | |
| 609 | cond_.notify_one(); |
| 610 | } |
| 611 | |
| 612 | void shutdown() override { |
| 613 | // Stop all worker threads... |
| 614 | { |
| 615 | std::unique_lock<std::mutex> lock(mutex_); |
| 616 | shutdown_ = true; |
| 617 | } |
| 618 | |
| 619 | cond_.notify_all(); |
| 620 | |
| 621 | // Join... |
| 622 | for (auto &t : threads_) { |
| 623 | t.join(); |
| 624 | } |
| 625 | } |
| 626 | |
| 627 | private: |
| 628 | struct worker { |
| 629 | explicit worker(ThreadPool &pool) : pool_(pool) {} |
| 630 | |
| 631 | void operator()() { |
| 632 | for (;;) { |
| 633 | std::function<void()> fn; |
| 634 | { |
| 635 | std::unique_lock<std::mutex> lock(pool_.mutex_); |
| 636 | |
| 637 | pool_.cond_.wait( |
| 638 | lock, [&] { return !pool_.jobs_.empty() || pool_.shutdown_; }); |
| 639 | |
| 640 | if (pool_.shutdown_ && pool_.jobs_.empty()) { break; } |
| 641 | |
| 642 | fn = std::move(pool_.jobs_.front()); |
| 643 | pool_.jobs_.pop_front(); |
| 644 | } |
| 645 | |
| 646 | assert(true == static_cast<bool>(fn)); |
| 647 | fn(); |
| 648 | } |
nothing calls this directly
no outgoing calls
no test coverage detected