Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(346)

Side by Side Diff: tools/gn/input_file_manager.cc

Issue 21114002: Add initial prototype for the GN meta-buildsystem. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: add owners and readme Created 7 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « tools/gn/input_file_manager.h ('k') | tools/gn/item.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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/input_file_manager.h"
6
7 #include "base/bind.h"
8 #include "base/stl_util.h"
9 #include "tools/gn/filesystem_utils.h"
10 #include "tools/gn/parser.h"
11 #include "tools/gn/scheduler.h"
12 #include "tools/gn/scope_per_file_provider.h"
13 #include "tools/gn/tokenizer.h"
14
15 namespace {
16
17 void InvokeFileLoadCallback(const InputFileManager::FileLoadCallback& cb,
18 const ParseNode* node) {
19 cb.Run(node);
20 }
21
22 } // namespace
23
24 InputFileManager::InputFileData::InputFileData(const SourceFile& file_name)
25 : file(file_name),
26 loaded(false),
27 sync_invocation(false) {
28 }
29
30 InputFileManager::InputFileData::~InputFileData() {
31 }
32
33 InputFileManager::InputFileManager() {
34 }
35
36 InputFileManager::~InputFileManager() {
37 // Should be single-threaded by now.
38 STLDeleteContainerPairSecondPointers(input_files_.begin(),
39 input_files_.end());
40 }
41
42 bool InputFileManager::AsyncLoadFile(const LocationRange& origin,
43 const BuildSettings* build_settings,
44 const SourceFile& file_name,
45 const FileLoadCallback& callback,
46 Err* err) {
47 // Try not to schedule callbacks while holding the lock. All cases that don't
48 // want to schedule should return early. Otherwise, this will be scheduled
49 // after we leave the lock.
50 base::Closure schedule_this;
51 {
52 base::AutoLock lock(lock_);
53
54 InputFileMap::const_iterator found = input_files_.find(file_name);
55 if (found == input_files_.end()) {
56 // New file, schedule load.
57 InputFileData* data = new InputFileData(file_name);
58 data->scheduled_callbacks.push_back(callback);
59 input_files_[file_name] = data;
60
61 schedule_this = base::Bind(&InputFileManager::BackgroundLoadFile,
62 this,
63 origin,
64 build_settings,
65 file_name,
66 &data->file);
67 } else {
68 InputFileData* data = found->second;
69
70 // Prevent mixing async and sync loads. See SyncLoadFile for discussion.
71 if (data->sync_invocation) {
72 g_scheduler->FailWithError(Err(
73 origin, "Load type mismatch.",
74 "The file \"" + file_name.value() + "\" was previously loaded\n"
75 "synchronously (via an import) and now you're trying to load it "
76 "asynchronously\n(via a deps rule). This is a class 2 misdemeanor: "
77 "a single input file must\nbe loaded the same way each time to "
78 "avoid blowing my tiny, tiny mind."));
79 return false;
80 }
81
82 if (data->loaded) {
83 // Can just directly issue the callback on the background thread.
84 schedule_this = base::Bind(&InvokeFileLoadCallback, callback,
85 data->parsed_root.get());
86 } else {
87 // Load is pending on this file, schedule the invoke.
88 data->scheduled_callbacks.push_back(callback);
89 return true;
90 }
91 }
92 }
93 g_scheduler->pool()->PostWorkerTaskWithShutdownBehavior(
94 FROM_HERE, schedule_this,
95 base::SequencedWorkerPool::BLOCK_SHUTDOWN);
96 return true;
97 }
98
99 const ParseNode* InputFileManager::SyncLoadFile(
100 const LocationRange& origin,
101 const BuildSettings* build_settings,
102 const SourceFile& file_name,
103 Err* err) {
104 base::AutoLock lock(lock_);
105
106 InputFileData* data = NULL;
107 InputFileMap::iterator found = input_files_.find(file_name);
108 if (found == input_files_.end()) {
109 base::AutoUnlock unlock(lock_);
110
111 // Haven't seen this file yet, start loading right now.
112 data = new InputFileData(file_name);
113 data->sync_invocation = true;
114 input_files_[file_name] = data;
115
116 if (!LoadFile(origin, build_settings, file_name, &data->file, err))
117 return NULL;
118 } else {
119 // This file has either been loaded or is pending loading.
120 data = found->second;
121
122 if (!data->sync_invocation) {
123 // Don't allow mixing of sync and async loads. If an async load is
124 // scheduled and then a bunch of threads need to load it synchronously
125 // and block on it loading, it could deadlock or at least cause a lot
126 // of wasted CPU while those threads wait for the load to complete (which
127 // may be far back in the input queue).
128 //
129 // We could work around this by promoting the load to a sync load. This
130 // requires a bunch of extra code to either check flags and likely do
131 // extra locking (bad) or to just do both types of load on the file and
132 // deal with the race condition.
133 //
134 // I have no practical way to test this, and generally we should have
135 // all include files processed synchronously and all build files
136 // processed asynchronously, so it doesn't happen in practice.
137 *err = Err(
138 origin, "Load type mismatch.",
139 "The file \"" + file_name.value() + "\" was previously loaded\n"
140 "asynchronously (via a deps rule) and now you're trying to load it "
141 "synchronously.\nThis is a class 2 misdemeanor: a single input file "
142 "must be loaded the same way\neach time to avoid blowing my tiny, "
143 "tiny mind.");
144 return NULL;
145 }
146
147 if (!data->loaded) {
148 // Wait for the already-pending sync load to complete.
149 if (!data->completion_event)
150 data->completion_event.reset(new base::WaitableEvent(false, false));
151 {
152 base::AutoUnlock unlock(lock_);
153 data->completion_event->Wait();
154 }
155 }
156 }
157
158 // The other load could have failed. In this case that error will be printed
159 // to the console, but we need to return something here, so make up a
160 // dummy error.
161 if (!data->parsed_root)
162 *err = Err(origin, "File parse failed");
163 return data->parsed_root.get();
164 }
165
166 int InputFileManager::GetInputFileCount() const {
167 base::AutoLock lock(lock_);
168 return input_files_.size();
169 }
170
171 void InputFileManager::GetAllInputFileNames(
172 std::vector<SourceFile>* result) const {
173 base::AutoLock lock(lock_);
174 result->reserve(input_files_.size());
175 for (InputFileMap::const_iterator i = input_files_.begin();
176 i != input_files_.end(); ++i) {
177 result->push_back(i->second->file.name());
178 }
179 }
180
181 void InputFileManager::BackgroundLoadFile(const LocationRange& origin,
182 const BuildSettings* build_settings,
183 const SourceFile& name,
184 InputFile* file) {
185 Err err;
186 if (!LoadFile(origin, build_settings, name, file, &err))
187 g_scheduler->FailWithError(err);
188 }
189
190 bool InputFileManager::LoadFile(const LocationRange& origin,
191 const BuildSettings* build_settings,
192 const SourceFile& name,
193 InputFile* file,
194 Err* err) {
195 // Do all of this stuff outside the lock. We should not give out file
196 // pointers until the read is complete.
197 if (g_scheduler->verbose_logging())
198 g_scheduler->Log("Loading", name.value());
199
200 // Read.
201 base::FilePath primary_path = build_settings->GetFullPath(name);
202 if (!file->Load(primary_path)) {
203 if (!build_settings->secondary_source_path().empty()) {
204 // Fall back to secondary source tree.
205 base::FilePath secondary_path =
206 build_settings->GetFullPathSecondary(name);
207 if (!file->Load(secondary_path)) {
208 *err = Err(origin, "Can't load input file.",
209 "Unable to load either \n" +
210 FilePathToUTF8(primary_path) + " or \n" +
211 FilePathToUTF8(secondary_path));
212 return false;
213 }
214 } else {
215 *err = Err(origin,
216 "Unable to load \"" + FilePathToUTF8(primary_path) + "\".");
217 return false;
218 }
219 }
220
221 if (g_scheduler->verbose_logging())
222 g_scheduler->Log("Parsing", name.value());
223
224 // Tokenize.
225 std::vector<Token> tokens = Tokenizer::Tokenize(file, err);
226 if (err->has_error())
227 return false;
228
229 // Parse.
230 scoped_ptr<ParseNode> root = Parser::Parse(tokens, err);
231 if (err->has_error())
232 return false;
233 ParseNode* unowned_root = root.get();
234
235 std::vector<FileLoadCallback> callbacks;
236 {
237 base::AutoLock lock(lock_);
238 DCHECK(input_files_.find(name) != input_files_.end());
239
240 InputFileData* data = input_files_[name];
241 data->loaded = true;
242 data->tokens.swap(tokens);
243 data->parsed_root = root.Pass();
244
245 callbacks.swap(data->scheduled_callbacks);
246 }
247
248 // Run pending invocations. Theoretically we could schedule each of these
249 // separately to get some parallelism. But normally there will only be one
250 // item in the list, so that's extra overhead and complexity for no gain.
251 for (size_t i = 0; i < callbacks.size(); i++)
252 callbacks[i].Run(unowned_root);
253 return true;
254 }
OLDNEW
« no previous file with comments | « tools/gn/input_file_manager.h ('k') | tools/gn/item.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698