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