Handle missing cached dependencies better

This commit is contained in:
Jacob Dufault 2017-10-12 08:40:51 -07:00
parent 4364a37d76
commit 272e23901c

View File

@ -500,7 +500,9 @@ struct Index_DoIdMap {
: current(std::move(current)), : current(std::move(current)),
perf(perf), perf(perf),
is_interactive(is_interactive), is_interactive(is_interactive),
write_to_disk(write_to_disk) {} write_to_disk(write_to_disk) {
assert(this->current);
}
}; };
struct Index_OnIdMapped { struct Index_OnIdMapped {
@ -647,6 +649,8 @@ struct CacheLoader {
return caches[path].get(); return caches[path].get();
} }
// Takes the existing cache or loads the cache at |path|. May return nullptr
// if the cache does not exist.
std::unique_ptr<IndexFile> TryTakeOrLoad(const std::string& path) { std::unique_ptr<IndexFile> TryTakeOrLoad(const std::string& path) {
auto it = caches.find(path); auto it = caches.find(path);
if (it != caches.end()) { if (it != caches.end()) {
@ -658,6 +662,14 @@ struct CacheLoader {
return LoadCachedIndex(config_, path); return LoadCachedIndex(config_, path);
} }
// Takes the existing cache or loads the cache at |path|. Asserts the cache
// exists.
std::unique_ptr<IndexFile> TakeOrLoad(const std::string& path) {
auto result = TryTakeOrLoad(path);
assert(result);
return result;
}
std::unordered_map<std::string, std::unique_ptr<IndexFile>> caches; std::unordered_map<std::string, std::unique_ptr<IndexFile>> caches;
Config* config_; Config* config_;
}; };
@ -736,7 +748,7 @@ struct IndexManager {
// IMPORT PIPELINE ///////////////////////////////////////////////////////////// // IMPORT PIPELINE /////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
enum class FileParseQuery { NeedsParse, DoesNotNeedParse, BadFile }; enum class FileParseQuery { NeedsParse, DoesNotNeedParse, NoSuchFile };
std::vector<Index_DoIdMap> DoParseFile( std::vector<Index_DoIdMap> DoParseFile(
Config* config, Config* config,
@ -772,12 +784,15 @@ std::vector<Index_DoIdMap> DoParseFile(
} }
optional<int64_t> modification_timestamp = GetLastModificationTime(path); optional<int64_t> modification_timestamp = GetLastModificationTime(path);
// Cannot find file.
if (!modification_timestamp) if (!modification_timestamp)
return FileParseQuery::BadFile; return FileParseQuery::NoSuchFile;
optional<int64_t> last_cached_modification = optional<int64_t> last_cached_modification =
timestamp_manager->GetLastCachedModificationTime(cache_loader, path); timestamp_manager->GetLastCachedModificationTime(cache_loader, path);
// File has been changed.
if (!last_cached_modification || if (!last_cached_modification ||
modification_timestamp != *last_cached_modification) { modification_timestamp != *last_cached_modification) {
file_consumer_shared->Reset(path); file_consumer_shared->Reset(path);
@ -785,21 +800,30 @@ std::vector<Index_DoIdMap> DoParseFile(
path, *modification_timestamp); path, *modification_timestamp);
return FileParseQuery::NeedsParse; return FileParseQuery::NeedsParse;
} }
// File has not changed, do not parse it.
return FileParseQuery::DoesNotNeedParse; return FileParseQuery::DoesNotNeedParse;
}; };
// Check timestamps and update |file_consumer_shared|. // Check timestamps and update |file_consumer_shared|.
FileParseQuery path_state = file_needs_parse(path, false /*is_dependency*/); FileParseQuery path_state = file_needs_parse(path, false /*is_dependency*/);
if (path_state == FileParseQuery::BadFile)
// Target file does not exist on disk, do not emit any indexes.
// TODO: Dependencies should be reassigned to other files. We can do this by
// updating the "primary_file" if it doesn't exist. Might not actually be a
// problem in practice.
if (path_state == FileParseQuery::NoSuchFile)
return result; return result;
bool needs_reparse = bool needs_reparse =
is_interactive || path_state == FileParseQuery::NeedsParse; is_interactive || path_state == FileParseQuery::NeedsParse;
for (const std::string& dependency : previous_index->dependencies) { for (const std::string& dependency : previous_index->dependencies) {
assert(!dependency.empty()); assert(!dependency.empty());
if (file_needs_parse(dependency, true /*is_dependency*/) == // note: Use != as there are multiple failure results for FileParseQuery.
FileParseQuery::NeedsParse) { if (file_needs_parse(dependency, true /*is_dependency*/) !=
FileParseQuery::DoesNotNeedParse) {
LOG_S(INFO) << "Timestamp has changed for " << dependency << " (via " LOG_S(INFO) << "Timestamp has changed for " << dependency << " (via "
<< previous_index->path << ")"; << previous_index->path << ")";
needs_reparse = true; needs_reparse = true;
@ -814,18 +838,29 @@ std::vector<Index_DoIdMap> DoParseFile(
// TODO/FIXME: real perf // TODO/FIXME: real perf
PerformanceImportFile perf; PerformanceImportFile perf;
result.push_back(Index_DoIdMap(cache_loader->TryTakeOrLoad(path), perf, result.push_back(Index_DoIdMap(cache_loader->TakeOrLoad(path), perf,
is_interactive, false /*write_to_disk*/)); is_interactive, false /*write_to_disk*/));
for (const std::string& dependency : previous_index->dependencies) { for (const std::string& dependency : previous_index->dependencies) {
// Only actually load the file if we haven't loaded it yet. Important // Only load a dependency if it is not already loaded.
// for perf when files have lots of common dependencies. //
// This is important for perf in large projects where there are lots of
// dependencies shared between many files.
if (!file_consumer_shared->Mark(dependency)) if (!file_consumer_shared->Mark(dependency))
continue; continue;
LOG_S(INFO) << "Emitting index result for " << dependency << " (via " LOG_S(INFO) << "Emitting index result for " << dependency << " (via "
<< previous_index->path << ")"; << previous_index->path << ")";
result.push_back(Index_DoIdMap(cache_loader->TryTakeOrLoad(dependency),
perf, is_interactive, std::unique_ptr<IndexFile> dependency_index =
cache_loader->TryTakeOrLoad(dependency);
// |dependency_index| may be null if there is no cache for it but
// another file has already started importing it.
if (!dependency_index)
continue;
result.push_back(Index_DoIdMap(std::move(dependency_index), perf,
is_interactive,
false /*write_to_disk*/)); false /*write_to_disk*/));
} }
return result; return result;
@ -878,9 +913,11 @@ std::vector<Index_DoIdMap> DoParseFile(
for (std::unique_ptr<IndexFile>& new_index : indexes) { for (std::unique_ptr<IndexFile>& new_index : indexes) {
Timer time; Timer time;
// Always emit diagnostics. This makes it easier to identify indexing // Only emit diagnostics for non-interactive sessions, which makes it easier
// problems. // to identify indexing problems. For interactive sessions, diagnostics are
EmitDiagnostics(working_files, new_index->path, new_index->diagnostics_); // handled by code completion.
if (!is_interactive)
EmitDiagnostics(working_files, new_index->path, new_index->diagnostics_);
// When main thread does IdMap request it will request the previous index if // When main thread does IdMap request it will request the previous index if
// needed. // needed.
@ -1145,6 +1182,8 @@ bool QueryDb_ImportMain(Config* config,
break; break;
did_work = true; did_work = true;
assert(request->current);
// If the request does not have previous state and we have already imported // If the request does not have previous state and we have already imported
// it, load the previous state from disk and rerun IdMap logic later. Do not // it, load the previous state from disk and rerun IdMap logic later. Do not
// do this if we have already attempted in the past. // do this if we have already attempted in the past.
@ -1172,8 +1211,6 @@ bool QueryDb_ImportMain(Config* config,
request->write_to_disk); request->write_to_disk);
Timer time; Timer time;
assert(request->current);
auto make_map = [db](std::unique_ptr<IndexFile> file) auto make_map = [db](std::unique_ptr<IndexFile> file)
-> std::unique_ptr<Index_OnIdMapped::File> { -> std::unique_ptr<Index_OnIdMapped::File> {
if (!file) if (!file)