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 |