OLD | NEW |
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "tools/gn/input_file_manager.h" | 5 #include "tools/gn/input_file_manager.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/stl_util.h" | 8 #include "base/stl_util.h" |
9 #include "tools/gn/filesystem_utils.h" | 9 #include "tools/gn/filesystem_utils.h" |
10 #include "tools/gn/parser.h" | 10 #include "tools/gn/parser.h" |
11 #include "tools/gn/scheduler.h" | 11 #include "tools/gn/scheduler.h" |
12 #include "tools/gn/scope_per_file_provider.h" | 12 #include "tools/gn/scope_per_file_provider.h" |
13 #include "tools/gn/tokenizer.h" | 13 #include "tools/gn/tokenizer.h" |
14 #include "tools/gn/trace.h" | 14 #include "tools/gn/trace.h" |
15 | 15 |
16 namespace { | 16 namespace { |
17 | 17 |
18 void InvokeFileLoadCallback(const InputFileManager::FileLoadCallback& cb, | 18 void InvokeFileLoadCallback(const InputFileManager::FileLoadCallback& cb, |
19 const ParseNode* node) { | 19 const ParseNode* node) { |
20 cb.Run(node); | 20 cb.Run(node); |
21 } | 21 } |
22 | 22 |
| 23 bool DoLoadFile(const LocationRange& origin, |
| 24 const BuildSettings* build_settings, |
| 25 const SourceFile& name, |
| 26 InputFile* file, |
| 27 std::vector<Token>* tokens, |
| 28 scoped_ptr<ParseNode>* root, |
| 29 Err* err) { |
| 30 // Do all of this stuff outside the lock. We should not give out file |
| 31 // pointers until the read is complete. |
| 32 if (g_scheduler->verbose_logging()) { |
| 33 std::string logmsg = name.value(); |
| 34 if (origin.begin().file()) |
| 35 logmsg += " (referenced from " + origin.begin().Describe(false) + ")"; |
| 36 g_scheduler->Log("Loading", logmsg); |
| 37 } |
| 38 |
| 39 // Read. |
| 40 base::FilePath primary_path = build_settings->GetFullPath(name); |
| 41 ScopedTrace load_trace(TraceItem::TRACE_FILE_LOAD, name.value()); |
| 42 if (!file->Load(primary_path)) { |
| 43 if (!build_settings->secondary_source_path().empty()) { |
| 44 // Fall back to secondary source tree. |
| 45 base::FilePath secondary_path = |
| 46 build_settings->GetFullPathSecondary(name); |
| 47 if (!file->Load(secondary_path)) { |
| 48 *err = Err(origin, "Can't load input file.", |
| 49 "Unable to load either \n" + |
| 50 FilePathToUTF8(primary_path) + " or \n" + |
| 51 FilePathToUTF8(secondary_path)); |
| 52 return false; |
| 53 } |
| 54 } else { |
| 55 *err = Err(origin, |
| 56 "Unable to load \"" + FilePathToUTF8(primary_path) + "\"."); |
| 57 return false; |
| 58 } |
| 59 } |
| 60 load_trace.Done(); |
| 61 |
| 62 ScopedTrace exec_trace(TraceItem::TRACE_FILE_PARSE, name.value()); |
| 63 |
| 64 // Tokenize. |
| 65 *tokens = Tokenizer::Tokenize(file, err); |
| 66 if (err->has_error()) |
| 67 return false; |
| 68 |
| 69 // Parse. |
| 70 *root = Parser::Parse(*tokens, err); |
| 71 if (err->has_error()) |
| 72 return false; |
| 73 |
| 74 exec_trace.Done(); |
| 75 return true; |
| 76 } |
| 77 |
23 } // namespace | 78 } // namespace |
24 | 79 |
25 InputFileManager::InputFileData::InputFileData(const SourceFile& file_name) | 80 InputFileManager::InputFileData::InputFileData(const SourceFile& file_name) |
26 : file(file_name), | 81 : file(file_name), |
27 loaded(false), | 82 loaded(false), |
28 sync_invocation(false) { | 83 sync_invocation(false) { |
29 } | 84 } |
30 | 85 |
31 InputFileManager::InputFileData::~InputFileData() { | 86 InputFileManager::InputFileData::~InputFileData() { |
32 } | 87 } |
(...skipping 171 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
204 Err err; | 259 Err err; |
205 if (!LoadFile(origin, build_settings, name, file, &err)) | 260 if (!LoadFile(origin, build_settings, name, file, &err)) |
206 g_scheduler->FailWithError(err); | 261 g_scheduler->FailWithError(err); |
207 } | 262 } |
208 | 263 |
209 bool InputFileManager::LoadFile(const LocationRange& origin, | 264 bool InputFileManager::LoadFile(const LocationRange& origin, |
210 const BuildSettings* build_settings, | 265 const BuildSettings* build_settings, |
211 const SourceFile& name, | 266 const SourceFile& name, |
212 InputFile* file, | 267 InputFile* file, |
213 Err* err) { | 268 Err* err) { |
214 // Do all of this stuff outside the lock. We should not give out file | 269 std::vector<Token> tokens; |
215 // pointers until the read is complete. | 270 scoped_ptr<ParseNode> root; |
216 if (g_scheduler->verbose_logging()) { | 271 bool success = DoLoadFile(origin, build_settings, name, file, |
217 std::string logmsg = name.value(); | 272 &tokens, &root, err); |
218 if (origin.begin().file()) | 273 // Can't return early. We have to ensure that the completion event is |
219 logmsg += " (referenced from " + origin.begin().Describe(false) + ")"; | 274 // signaled in all cases bacause another thread could be blocked on this one. |
220 g_scheduler->Log("Loading", logmsg); | |
221 } | |
222 | 275 |
223 // Read. | 276 // Save this pointer for running the callbacks below, which happens after the |
224 base::FilePath primary_path = build_settings->GetFullPath(name); | 277 // scoped ptr ownership is taken away inside the lock. |
225 ScopedTrace load_trace(TraceItem::TRACE_FILE_LOAD, name.value()); | |
226 if (!file->Load(primary_path)) { | |
227 if (!build_settings->secondary_source_path().empty()) { | |
228 // Fall back to secondary source tree. | |
229 base::FilePath secondary_path = | |
230 build_settings->GetFullPathSecondary(name); | |
231 if (!file->Load(secondary_path)) { | |
232 *err = Err(origin, "Can't load input file.", | |
233 "Unable to load either \n" + | |
234 FilePathToUTF8(primary_path) + " or \n" + | |
235 FilePathToUTF8(secondary_path)); | |
236 return false; | |
237 } | |
238 } else { | |
239 *err = Err(origin, | |
240 "Unable to load \"" + FilePathToUTF8(primary_path) + "\"."); | |
241 return false; | |
242 } | |
243 } | |
244 load_trace.Done(); | |
245 | |
246 ScopedTrace exec_trace(TraceItem::TRACE_FILE_PARSE, name.value()); | |
247 | |
248 // Tokenize. | |
249 std::vector<Token> tokens = Tokenizer::Tokenize(file, err); | |
250 if (err->has_error()) | |
251 return false; | |
252 | |
253 // Parse. | |
254 scoped_ptr<ParseNode> root = Parser::Parse(tokens, err); | |
255 if (err->has_error()) | |
256 return false; | |
257 ParseNode* unowned_root = root.get(); | 278 ParseNode* unowned_root = root.get(); |
258 | 279 |
259 exec_trace.Done(); | |
260 | |
261 std::vector<FileLoadCallback> callbacks; | 280 std::vector<FileLoadCallback> callbacks; |
262 { | 281 { |
263 base::AutoLock lock(lock_); | 282 base::AutoLock lock(lock_); |
264 DCHECK(input_files_.find(name) != input_files_.end()); | 283 DCHECK(input_files_.find(name) != input_files_.end()); |
265 | 284 |
266 InputFileData* data = input_files_[name]; | 285 InputFileData* data = input_files_[name]; |
267 data->loaded = true; | 286 data->loaded = true; |
268 data->tokens.swap(tokens); | 287 if (success) { |
269 data->parsed_root = root.Pass(); | 288 data->tokens.swap(tokens); |
| 289 data->parsed_root = root.Pass(); |
| 290 } |
270 | 291 |
271 // Unblock waiters on this event. | 292 // Unblock waiters on this event. |
272 // | 293 // |
273 // It's somewhat bad to signal this inside the lock. When it's used, it's | 294 // It's somewhat bad to signal this inside the lock. When it's used, it's |
274 // lazily created inside the lock. So we need to do the check and signal | 295 // lazily created inside the lock. So we need to do the check and signal |
275 // inside the lock to avoid race conditions on the lazy creation of the | 296 // inside the lock to avoid race conditions on the lazy creation of the |
276 // lock. | 297 // lock. |
277 // | 298 // |
278 // We could avoid this by creating the lock every time, but the lock is | 299 // We could avoid this by creating the lock every time, but the lock is |
279 // very seldom used and will generally be NULL, so my current theory is that | 300 // very seldom used and will generally be NULL, so my current theory is that |
280 // several signals of a completion event inside a lock is better than | 301 // several signals of a completion event inside a lock is better than |
281 // creating about 1000 extra locks (one for each file). | 302 // creating about 1000 extra locks (one for each file). |
282 if (data->completion_event) | 303 if (data->completion_event) |
283 data->completion_event->Signal(); | 304 data->completion_event->Signal(); |
284 | 305 |
285 callbacks.swap(data->scheduled_callbacks); | 306 callbacks.swap(data->scheduled_callbacks); |
286 } | 307 } |
287 | 308 |
288 // Run pending invocations. Theoretically we could schedule each of these | 309 // Run pending invocations. Theoretically we could schedule each of these |
289 // separately to get some parallelism. But normally there will only be one | 310 // separately to get some parallelism. But normally there will only be one |
290 // item in the list, so that's extra overhead and complexity for no gain. | 311 // item in the list, so that's extra overhead and complexity for no gain. |
291 for (size_t i = 0; i < callbacks.size(); i++) | 312 if (success) { |
292 callbacks[i].Run(unowned_root); | 313 for (size_t i = 0; i < callbacks.size(); i++) |
293 return true; | 314 callbacks[i].Run(unowned_root); |
| 315 } |
| 316 return success; |
294 } | 317 } |
OLD | NEW |