| 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/import_manager.h" | 5 #include "tools/gn/import_manager.h" |
| 6 | 6 |
| 7 #include "tools/gn/err.h" |
| 7 #include "tools/gn/parse_tree.h" | 8 #include "tools/gn/parse_tree.h" |
| 8 #include "tools/gn/scheduler.h" | 9 #include "tools/gn/scheduler.h" |
| 9 #include "tools/gn/scope_per_file_provider.h" | 10 #include "tools/gn/scope_per_file_provider.h" |
| 10 | 11 |
| 11 namespace { | 12 namespace { |
| 12 | 13 |
| 13 // Returns a newly-allocated scope on success, null on failure. | 14 // Returns a newly-allocated scope on success, null on failure. |
| 14 std::unique_ptr<Scope> UncachedImport(const Settings* settings, | 15 std::unique_ptr<Scope> UncachedImport(const Settings* settings, |
| 15 const SourceFile& file, | 16 const SourceFile& file, |
| 16 const ParseNode* node_for_err, | 17 const ParseNode* node_for_err, |
| (...skipping 17 matching lines...) Expand all Loading... |
| 34 // If there was an error, append the caller location so the error message | 35 // If there was an error, append the caller location so the error message |
| 35 // displays a why the file was imported (esp. useful for failed asserts). | 36 // displays a why the file was imported (esp. useful for failed asserts). |
| 36 err->AppendSubErr(Err(node_for_err, "whence it was imported.")); | 37 err->AppendSubErr(Err(node_for_err, "whence it was imported.")); |
| 37 return nullptr; | 38 return nullptr; |
| 38 } | 39 } |
| 39 scope->ClearProcessingImport(); | 40 scope->ClearProcessingImport(); |
| 40 | 41 |
| 41 return scope; | 42 return scope; |
| 42 } | 43 } |
| 43 | 44 |
| 44 } // namesapce | 45 } // namespace |
| 46 |
| 47 struct ImportManager::ImportInfo { |
| 48 ImportInfo() {} |
| 49 ~ImportInfo() {} |
| 50 |
| 51 // This lock protects the unique_ptr. Once the scope is computed, |
| 52 // it is const and can be accessed read-only outside of the lock. |
| 53 base::Lock load_lock; |
| 54 |
| 55 std::unique_ptr<const Scope> scope; |
| 56 |
| 57 // The result of loading the import. If the load failed, the scope will be |
| 58 // null but this will be set to error. In this case the thread should not |
| 59 // attempt to load the file, even if the scope is null. |
| 60 Err load_result; |
| 61 }; |
| 45 | 62 |
| 46 ImportManager::ImportManager() { | 63 ImportManager::ImportManager() { |
| 47 } | 64 } |
| 48 | 65 |
| 49 ImportManager::~ImportManager() { | 66 ImportManager::~ImportManager() { |
| 50 } | 67 } |
| 51 | 68 |
| 52 bool ImportManager::DoImport(const SourceFile& file, | 69 bool ImportManager::DoImport(const SourceFile& file, |
| 53 const ParseNode* node_for_err, | 70 const ParseNode* node_for_err, |
| 54 Scope* scope, | 71 Scope* scope, |
| 55 Err* err) { | 72 Err* err) { |
| 56 // See if we have a cached import, but be careful to actually do the scope | 73 // See if we have a cached import, but be careful to actually do the scope |
| 57 // copying outside of the lock. | 74 // copying outside of the lock. |
| 58 const Scope* imported_scope = nullptr; | 75 ImportInfo* import_info = nullptr; |
| 59 { | 76 { |
| 60 base::AutoLock lock(lock_); | 77 base::AutoLock lock(imports_lock_); |
| 61 ImportMap::const_iterator found = imports_.find(file); | 78 std::unique_ptr<ImportInfo>& info_ptr = imports_[file]; |
| 62 if (found != imports_.end()) | 79 if (!info_ptr) |
| 63 imported_scope = found->second.get(); | 80 info_ptr.reset(new ImportInfo); |
| 81 |
| 82 // Promote the ImportInfo to outside of the imports lock. |
| 83 import_info = info_ptr.get(); |
| 64 } | 84 } |
| 65 | 85 |
| 66 if (!imported_scope) { | 86 // Now use the per-import-file lock to block this thread if another thread |
| 67 // Do a new import of the file. | 87 // is already processing the import. |
| 68 std::unique_ptr<Scope> new_imported_scope = | 88 const Scope* import_scope = nullptr; |
| 69 UncachedImport(scope->settings(), file, node_for_err, err); | 89 { |
| 70 if (!new_imported_scope) | 90 base::AutoLock lock(import_info->load_lock); |
| 71 return false; | |
| 72 | 91 |
| 73 // We loaded the file outside the lock. This means that there could be a | 92 if (!import_info->scope) { |
| 74 // race and the file was already loaded on a background thread. Recover | 93 // Only load if the import hasn't already failed. |
| 75 // from this and use the existing one if that happens. | 94 if (!import_info->load_result.has_error()) { |
| 76 { | 95 import_info->scope = UncachedImport( |
| 77 base::AutoLock lock(lock_); | 96 scope->settings(), file, node_for_err, &import_info->load_result); |
| 78 ImportMap::const_iterator found = imports_.find(file); | 97 } |
| 79 if (found != imports_.end()) { | 98 if (import_info->load_result.has_error()) { |
| 80 imported_scope = found->second.get(); | 99 *err = import_info->load_result; |
| 81 } else { | 100 return false; |
| 82 imported_scope = new_imported_scope.get(); | |
| 83 imports_[file] = std::move(new_imported_scope); | |
| 84 } | 101 } |
| 85 } | 102 } |
| 103 |
| 104 // Promote the now-read-only scope to outside the load lock. |
| 105 import_scope = import_info->scope.get(); |
| 86 } | 106 } |
| 87 | 107 |
| 88 Scope::MergeOptions options; | 108 Scope::MergeOptions options; |
| 89 options.skip_private_vars = true; | 109 options.skip_private_vars = true; |
| 90 options.mark_dest_used = true; // Don't require all imported values be used. | 110 options.mark_dest_used = true; // Don't require all imported values be used. |
| 91 return imported_scope->NonRecursiveMergeTo(scope, options, node_for_err, | 111 return import_scope->NonRecursiveMergeTo(scope, options, node_for_err, |
| 92 "import", err); | 112 "import", err); |
| 93 } | 113 } |
| OLD | NEW |