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 "tools/gn/scheduler.h" 6 7 #include "base/bind.h" 8 #include "base/command_line.h" 9 #include "base/strings/string_number_conversions.h" 10 #include "tools/gn/standard_out.h" 11 12 Scheduler* g_scheduler = NULL; 13 14 namespace { 15 16 int GetThreadCount() { 17 std::string thread_count = 18 CommandLine::ForCurrentProcess()->GetSwitchValueASCII("threads"); 19 20 int result; 21 if (thread_count.empty() || !base::StringToInt(thread_count, &result)) 22 return 32; 23 return result; 24 } 25 26 } // namespace 27 28 Scheduler::Scheduler() 29 : pool_(new base::SequencedWorkerPool(GetThreadCount(), "worker_")), 30 input_file_manager_(new InputFileManager), 31 verbose_logging_(false), 32 work_count_(0), 33 is_failed_(false), 34 has_been_shutdown_(false) { 35 g_scheduler = this; 36 } 37 38 Scheduler::~Scheduler() { 39 if (!has_been_shutdown_) 40 pool_->Shutdown(); 41 g_scheduler = NULL; 42 } 43 44 bool Scheduler::Run() { 45 runner_.Run(); 46 bool local_is_failed; 47 { 48 base::AutoLock lock(lock_); 49 local_is_failed = is_failed(); 50 has_been_shutdown_ = true; 51 } 52 // Don't do this inside the lock since it will block on the workers, which 53 // may be in turn waiting on the lock. 54 pool_->Shutdown(); 55 return !local_is_failed; 56 } 57 58 void Scheduler::Log(const std::string& verb, const std::string& msg) { 59 if (base::MessageLoop::current() == &main_loop_) { 60 LogOnMainThread(verb, msg); 61 } else { 62 // The run loop always joins on the sub threads, so the lifetime of this 63 // object outlives the invocations of this function, hence "unretained". 64 main_loop_.PostTask(FROM_HERE, 65 base::Bind(&Scheduler::LogOnMainThread, 66 base::Unretained(this), verb, msg)); 67 } 68 } 69 70 void Scheduler::FailWithError(const Err& err) { 71 DCHECK(err.has_error()); 72 { 73 base::AutoLock lock(lock_); 74 75 if (is_failed_ || has_been_shutdown_) 76 return; // Ignore errors once we see one. 77 is_failed_ = true; 78 } 79 80 if (base::MessageLoop::current() == &main_loop_) { 81 FailWithErrorOnMainThread(err); 82 } else { 83 // The run loop always joins on the sub threads, so the lifetime of this 84 // object outlives the invocations of this function, hence "unretained". 85 main_loop_.PostTask(FROM_HERE, 86 base::Bind(&Scheduler::FailWithErrorOnMainThread, 87 base::Unretained(this), err)); 88 } 89 } 90 91 void Scheduler::ScheduleWork(const base::Closure& work) { 92 IncrementWorkCount(); 93 pool_->PostWorkerTaskWithShutdownBehavior( 94 FROM_HERE, base::Bind(&Scheduler::DoWork, 95 base::Unretained(this), work), 96 base::SequencedWorkerPool::BLOCK_SHUTDOWN); 97 } 98 99 void Scheduler::AddGenDependency(const base::FilePath& file) { 100 base::AutoLock lock(lock_); 101 gen_dependencies_.push_back(file); 102 } 103 104 std::vector<base::FilePath> Scheduler::GetGenDependencies() const { 105 base::AutoLock lock(lock_); 106 return gen_dependencies_; 107 } 108 109 void Scheduler::IncrementWorkCount() { 110 base::AtomicRefCountInc(&work_count_); 111 } 112 113 void Scheduler::DecrementWorkCount() { 114 if (!base::AtomicRefCountDec(&work_count_)) { 115 if (base::MessageLoop::current() == &main_loop_) { 116 OnComplete(); 117 } else { 118 main_loop_.PostTask(FROM_HERE, 119 base::Bind(&Scheduler::OnComplete, 120 base::Unretained(this))); 121 } 122 } 123 } 124 125 void Scheduler::LogOnMainThread(const std::string& verb, 126 const std::string& msg) { 127 OutputString(verb, DECORATION_YELLOW); 128 OutputString(" " + msg + "\n"); 129 } 130 131 void Scheduler::FailWithErrorOnMainThread(const Err& err) { 132 err.PrintToStdout(); 133 runner_.Quit(); 134 } 135 136 void Scheduler::DoWork(const base::Closure& closure) { 137 closure.Run(); 138 DecrementWorkCount(); 139 } 140 141 void Scheduler::OnComplete() { 142 // Should be called on the main thread. 143 DCHECK(base::MessageLoop::current() == main_loop()); 144 runner_.Quit(); 145 } 146