• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2013 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 "gn/scheduler.h"
6 
7 #include <algorithm>
8 
9 #include "gn/standard_out.h"
10 #include "gn/target.h"
11 
12 namespace {}  // namespace
13 
14 Scheduler* g_scheduler = nullptr;
15 
Scheduler()16 Scheduler::Scheduler()
17     : main_thread_run_loop_(MsgLoop::Current()),
18       input_file_manager_(new InputFileManager) {
19   g_scheduler = this;
20 }
21 
~Scheduler()22 Scheduler::~Scheduler() {
23   WaitForPoolTasks();
24   g_scheduler = nullptr;
25 }
26 
Run()27 bool Scheduler::Run() {
28   main_thread_run_loop_->Run();
29   bool local_is_failed;
30   {
31     std::lock_guard<std::mutex> lock(lock_);
32     local_is_failed = is_failed();
33     has_been_shutdown_ = true;
34   }
35   // Don't do this while holding |lock_|, since it will block on the workers,
36   // which may be in turn waiting on the lock.
37   WaitForPoolTasks();
38   return !local_is_failed;
39 }
40 
Log(const std::string & verb,const std::string & msg)41 void Scheduler::Log(const std::string& verb, const std::string& msg) {
42   task_runner()->PostTask([this, verb, msg]() { LogOnMainThread(verb, msg); });
43 }
44 
FailWithError(const Err & err)45 void Scheduler::FailWithError(const Err& err) {
46   DCHECK(err.has_error());
47   {
48     std::lock_guard<std::mutex> lock(lock_);
49 
50     if (is_failed_ || has_been_shutdown_)
51       return;  // Ignore errors once we see one.
52     is_failed_ = true;
53   }
54 
55   task_runner()->PostTask([this, err]() { FailWithErrorOnMainThread(err); });
56 }
57 
ScheduleWork(std::function<void ()> work)58 void Scheduler::ScheduleWork(std::function<void()> work) {
59   IncrementWorkCount();
60   pool_work_count_.Increment();
61   worker_pool_.PostTask([this, work = std::move(work)]() {
62     work();
63     DecrementWorkCount();
64     if (!pool_work_count_.Decrement()) {
65       std::unique_lock<std::mutex> auto_lock(pool_work_count_lock_);
66       pool_work_count_cv_.notify_one();
67     }
68   });
69 }
70 
AddGenDependency(const base::FilePath & file)71 void Scheduler::AddGenDependency(const base::FilePath& file) {
72   std::lock_guard<std::mutex> lock(lock_);
73   gen_dependencies_.push_back(file);
74 }
75 
GetGenDependencies() const76 std::vector<base::FilePath> Scheduler::GetGenDependencies() const {
77   std::lock_guard<std::mutex> lock(lock_);
78   return gen_dependencies_;
79 }
80 
AddWrittenFile(const SourceFile & file)81 void Scheduler::AddWrittenFile(const SourceFile& file) {
82   std::lock_guard<std::mutex> lock(lock_);
83   written_files_.push_back(file);
84 }
85 
AddUnknownGeneratedInput(const Target * target,const SourceFile & file)86 void Scheduler::AddUnknownGeneratedInput(const Target* target,
87                                          const SourceFile& file) {
88   std::lock_guard<std::mutex> lock(lock_);
89   unknown_generated_inputs_.insert(std::make_pair(file, target));
90 }
91 
AddWriteRuntimeDepsTarget(const Target * target)92 void Scheduler::AddWriteRuntimeDepsTarget(const Target* target) {
93   std::lock_guard<std::mutex> lock(lock_);
94   write_runtime_deps_targets_.push_back(target);
95 }
96 
GetWriteRuntimeDepsTargets() const97 std::vector<const Target*> Scheduler::GetWriteRuntimeDepsTargets() const {
98   std::lock_guard<std::mutex> lock(lock_);
99   return write_runtime_deps_targets_;
100 }
101 
IsFileGeneratedByWriteRuntimeDeps(const OutputFile & file) const102 bool Scheduler::IsFileGeneratedByWriteRuntimeDeps(
103     const OutputFile& file) const {
104   std::lock_guard<std::mutex> lock(lock_);
105   // Number of targets should be quite small, so brute-force search is fine.
106   for (const Target* target : write_runtime_deps_targets_) {
107     if (file == target->write_runtime_deps_output()) {
108       return true;
109     }
110   }
111   return false;
112 }
113 
AddGeneratedFile(const SourceFile & entry)114 void Scheduler::AddGeneratedFile(const SourceFile& entry) {
115   std::lock_guard<std::mutex> lock(lock_);
116   generated_files_.insert(std::make_pair(entry, true));
117 }
118 
IsFileGeneratedByTarget(const SourceFile & file) const119 bool Scheduler::IsFileGeneratedByTarget(const SourceFile& file) const {
120   std::lock_guard<std::mutex> lock(lock_);
121   return generated_files_.find(file) != generated_files_.end();
122 }
123 
GetUnknownGeneratedInputs() const124 std::multimap<SourceFile, const Target*> Scheduler::GetUnknownGeneratedInputs()
125     const {
126   std::lock_guard<std::mutex> lock(lock_);
127 
128   // Remove all unknown inputs that were written files. These are OK as inputs
129   // to build steps since they were written as a side-effect of running GN.
130   //
131   // It's assumed that this function is called once during cleanup to check for
132   // errors, so performing this work in the lock doesn't matter.
133   std::multimap<SourceFile, const Target*> filtered = unknown_generated_inputs_;
134   for (const SourceFile& file : written_files_)
135     filtered.erase(file);
136 
137   return filtered;
138 }
139 
ClearUnknownGeneratedInputsAndWrittenFiles()140 void Scheduler::ClearUnknownGeneratedInputsAndWrittenFiles() {
141   std::lock_guard<std::mutex> lock(lock_);
142   unknown_generated_inputs_.clear();
143   written_files_.clear();
144 }
145 
IncrementWorkCount()146 void Scheduler::IncrementWorkCount() {
147   work_count_.Increment();
148 }
149 
DecrementWorkCount()150 void Scheduler::DecrementWorkCount() {
151   if (!work_count_.Decrement()) {
152     task_runner()->PostTask([this]() { OnComplete(); });
153   }
154 }
155 
SuppressOutputForTesting(bool suppress)156 void Scheduler::SuppressOutputForTesting(bool suppress) {
157   std::lock_guard<std::mutex> lock(lock_);
158   suppress_output_for_testing_ = suppress;
159 }
160 
LogOnMainThread(const std::string & verb,const std::string & msg)161 void Scheduler::LogOnMainThread(const std::string& verb,
162                                 const std::string& msg) {
163   OutputString(verb, DECORATION_YELLOW);
164   OutputString(" " + msg + "\n");
165 }
166 
FailWithErrorOnMainThread(const Err & err)167 void Scheduler::FailWithErrorOnMainThread(const Err& err) {
168   if (!suppress_output_for_testing_)
169     err.PrintToStdout();
170   task_runner()->PostQuit();
171 }
172 
OnComplete()173 void Scheduler::OnComplete() {
174   // Should be called on the main thread.
175   DCHECK(task_runner() == MsgLoop::Current());
176   task_runner()->PostQuit();
177 }
178 
WaitForPoolTasks()179 void Scheduler::WaitForPoolTasks() {
180   std::unique_lock<std::mutex> lock(pool_work_count_lock_);
181   while (!pool_work_count_.IsZero())
182     pool_work_count_cv_.wait(lock);
183 }
184