| 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 |