• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "gn/import_manager.h"
6 
7 #include <memory>
8 
9 #include "gn/err.h"
10 #include "gn/parse_tree.h"
11 #include "gn/scheduler.h"
12 #include "gn/scope_per_file_provider.h"
13 #include "gn/trace.h"
14 #include "util/ticks.h"
15 
16 namespace {
17 
18 // Returns a newly-allocated scope on success, null on failure.
UncachedImport(const Settings * settings,const SourceFile & file,const ParseNode * node_for_err,Err * err)19 std::unique_ptr<Scope> UncachedImport(const Settings* settings,
20                                       const SourceFile& file,
21                                       const ParseNode* node_for_err,
22                                       Err* err) {
23   ScopedTrace load_trace(TraceItem::TRACE_IMPORT_LOAD, file.value());
24   load_trace.SetToolchain(settings->toolchain_label());
25 
26   const ParseNode* node = g_scheduler->input_file_manager()->SyncLoadFile(
27       node_for_err->GetRange(), settings->build_settings(), file, err);
28   if (!node)
29     return nullptr;
30 
31   std::unique_ptr<Scope> scope =
32       std::make_unique<Scope>(settings->base_config());
33   scope->set_source_dir(file.GetDir());
34 
35   // Don't allow ScopePerFileProvider to provide target-related variables.
36   // These will be relative to the imported file, which is probably not what
37   // people mean when they use these.
38   ScopePerFileProvider per_file_provider(scope.get(), false);
39 
40   scope->SetProcessingImport();
41   node->Execute(scope.get(), err);
42   if (err->has_error()) {
43     // If there was an error, append the caller location so the error message
44     // displays a why the file was imported (esp. useful for failed asserts).
45     err->AppendSubErr(Err(node_for_err, "whence it was imported."));
46     return nullptr;
47   }
48   scope->ClearProcessingImport();
49 
50   return scope;
51 }
52 
53 }  // namespace
54 
55 struct ImportManager::ImportInfo {
56   ImportInfo() = default;
57   ~ImportInfo() = default;
58 
59   // This lock protects the unique_ptr. Once the scope is computed,
60   // it is const and can be accessed read-only outside of the lock.
61   std::mutex load_lock;
62 
63   std::unique_ptr<const Scope> scope;
64 
65   // The result of loading the import. If the load failed, the scope will be
66   // null but this will be set to error. In this case the thread should not
67   // attempt to load the file, even if the scope is null.
68   Err load_result;
69 };
70 
71 ImportManager::ImportManager() = default;
72 
73 ImportManager::~ImportManager() = default;
74 
DoImport(const SourceFile & file,const ParseNode * node_for_err,Scope * scope,Err * err)75 bool ImportManager::DoImport(const SourceFile& file,
76                              const ParseNode* node_for_err,
77                              Scope* scope,
78                              Err* err) {
79   // Key for the current import on the current thread in imports_in_progress_.
80   std::stringstream ss;
81   ss << std::this_thread::get_id() << file.value();
82   std::string key = ss.str();
83 
84   // See if we have a cached import, but be careful to actually do the scope
85   // copying outside of the lock.
86   ImportInfo* import_info = nullptr;
87   {
88     std::lock_guard<std::mutex> lock(imports_lock_);
89     std::unique_ptr<ImportInfo>& info_ptr = imports_[file];
90     if (!info_ptr)
91       info_ptr = std::make_unique<ImportInfo>();
92 
93     // Promote the ImportInfo to outside of the imports lock.
94     import_info = info_ptr.get();
95 
96     if (imports_in_progress_.find(key) != imports_in_progress_.end()) {
97       *err = Err(Location(), file.value() + " is part of an import loop.");
98       return false;
99     }
100     imports_in_progress_.insert(key);
101   }
102 
103   // Now use the per-import-file lock to block this thread if another thread
104   // is already processing the import.
105   const Scope* import_scope = nullptr;
106   {
107     Ticks import_block_begin = TicksNow();
108     std::lock_guard<std::mutex> lock(import_info->load_lock);
109 
110     if (!import_info->scope) {
111       // Only load if the import hasn't already failed.
112       if (!import_info->load_result.has_error()) {
113         import_info->scope = UncachedImport(
114             scope->settings(), file, node_for_err, &import_info->load_result);
115       }
116       if (import_info->load_result.has_error()) {
117         *err = import_info->load_result;
118         return false;
119       }
120     } else {
121       // Add trace if this thread was blocked for a long period of time and did
122       // not load the import itself.
123       Ticks import_block_end = TicksNow();
124       constexpr auto kImportBlockTraceThresholdMS = 20;
125       if (TracingEnabled() &&
126           TicksDelta(import_block_end, import_block_begin).InMilliseconds() >
127               kImportBlockTraceThresholdMS) {
128         auto import_block_trace = std::make_unique<TraceItem>(
129             TraceItem::TRACE_IMPORT_BLOCK, file.value(),
130             std::this_thread::get_id());
131         import_block_trace->set_begin(import_block_begin);
132         import_block_trace->set_end(import_block_end);
133         import_block_trace->set_toolchain(
134             scope->settings()->toolchain_label().GetUserVisibleName(false));
135         AddTrace(std::move(import_block_trace));
136       }
137     }
138 
139     // Promote the now-read-only scope to outside the load lock.
140     import_scope = import_info->scope.get();
141   }
142 
143   Scope::MergeOptions options;
144   options.skip_private_vars = true;
145   options.mark_dest_used = true;  // Don't require all imported values be used.
146 
147   {
148     std::lock_guard<std::mutex> lock(imports_lock_);
149     imports_in_progress_.erase(key);
150   }
151 
152   return import_scope->NonRecursiveMergeTo(scope, options, node_for_err,
153                                            "import", err);
154 }
155 
GetImportedFiles() const156 std::vector<SourceFile> ImportManager::GetImportedFiles() const {
157   std::vector<SourceFile> imported_files;
158   imported_files.resize(imports_.size());
159   std::transform(imports_.begin(), imports_.end(), imported_files.begin(),
160                  [](const ImportMap::value_type& val) { return val.first; });
161   return imported_files;
162 }
163