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