xref: /third_party/gn/src/util/worker_pool.cc (revision 6d528ed9)
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
12namespace {
13
14int 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
46WorkerPool::WorkerPool() : WorkerPool(GetThreadCount()) {}
47
48WorkerPool::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
54WorkerPool::~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
67void 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
77void 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