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
12Scheduler* g_scheduler = NULL;
13
14namespace {
15
16int 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
28Scheduler::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
38Scheduler::~Scheduler() {
39  if (!has_been_shutdown_)
40    pool_->Shutdown();
41  g_scheduler = NULL;
42}
43
44bool 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
58void 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
70void 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
91void 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
99void Scheduler::AddGenDependency(const base::FilePath& file) {
100  base::AutoLock lock(lock_);
101  gen_dependencies_.push_back(file);
102}
103
104std::vector<base::FilePath> Scheduler::GetGenDependencies() const {
105  base::AutoLock lock(lock_);
106  return gen_dependencies_;
107}
108
109void Scheduler::IncrementWorkCount() {
110  base::AtomicRefCountInc(&work_count_);
111}
112
113void 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
125void Scheduler::LogOnMainThread(const std::string& verb,
126                                const std::string& msg) {
127  OutputString(verb, DECORATION_YELLOW);
128  OutputString(" " + msg + "\n");
129}
130
131void Scheduler::FailWithErrorOnMainThread(const Err& err) {
132  err.PrintToStdout();
133  runner_.Quit();
134}
135
136void Scheduler::DoWork(const base::Closure& closure) {
137  closure.Run();
138  DecrementWorkCount();
139}
140
141void Scheduler::OnComplete() {
142  // Should be called on the main thread.
143  DCHECK(base::MessageLoop::current() == main_loop());
144  runner_.Quit();
145}
146