Home | History | Annotate | Download | only in gn
      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