• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "util/worker_pool.h"
6 
7 #include "base/command_line.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "gn/switches.h"
10 #include "util/sys_info.h"
11 
12 namespace {
13 
GetThreadCount()14 int GetThreadCount() {
15   std::string thread_count =
16       base::CommandLine::ForCurrentProcess()->GetSwitchValueString(
17           switches::kThreads);
18 
19   // See if an override was specified on the command line.
20   int result;
21   if (!thread_count.empty() && base::StringToInt(thread_count, &result) &&
22       result >= 1) {
23     return result;
24   }
25 
26   // Base the default number of worker threads on number of cores in the
27   // system. When building large projects, the speed can be limited by how fast
28   // the main thread can dispatch work and connect the dependency graph. If
29   // there are too many worker threads, the main thread can be starved and it
30   // will run slower overall.
31   //
32   // One less worker thread than the number of physical CPUs seems to be a
33   // good value, both theoretically and experimentally. But always use at
34   // least some workers to prevent us from being too sensitive to I/O latency
35   // on low-end systems.
36   //
37   // The minimum thread count is based on measuring the optimal threads for the
38   // Chrome build on a several-year-old 4-core MacBook.
39   // Almost all CPUs now are hyperthreaded.
40   int num_cores = NumberOfProcessors() / 2;
41   return std::max(num_cores - 1, 8);
42 }
43 
44 }  // namespace
45 
WorkerPool()46 WorkerPool::WorkerPool() : WorkerPool(GetThreadCount()) {}
47 
WorkerPool(size_t thread_count)48 WorkerPool::WorkerPool(size_t thread_count) : should_stop_processing_(false) {
49   threads_.reserve(thread_count);
50   for (size_t i = 0; i < thread_count; ++i)
51     threads_.emplace_back([this]() { Worker(); });
52 }
53 
~WorkerPool()54 WorkerPool::~WorkerPool() {
55   {
56     std::unique_lock<std::mutex> queue_lock(queue_mutex_);
57     should_stop_processing_ = true;
58   }
59 
60   pool_notifier_.notify_all();
61 
62   for (auto& task_thread : threads_) {
63     task_thread.join();
64   }
65 }
66 
PostTask(std::function<void ()> work)67 void WorkerPool::PostTask(std::function<void()> work) {
68   {
69     std::unique_lock<std::mutex> queue_lock(queue_mutex_);
70     CHECK(!should_stop_processing_);
71     task_queue_.emplace(std::move(work));
72   }
73 
74   pool_notifier_.notify_one();
75 }
76 
Worker()77 void WorkerPool::Worker() {
78   for (;;) {
79     std::function<void()> task;
80 
81     {
82       std::unique_lock<std::mutex> queue_lock(queue_mutex_);
83 
84       pool_notifier_.wait(queue_lock, [this]() {
85         return (!task_queue_.empty()) || should_stop_processing_;
86       });
87 
88       if (should_stop_processing_ && task_queue_.empty())
89         return;
90 
91       task = std::move(task_queue_.front());
92       task_queue_.pop();
93     }
94 
95     task();
96   }
97 }
98