llvm-project/clang-tools-extra/clangd/index/BackgroundRebuild.cpp

138 lines
4.2 KiB
C++

//===-- BackgroundRebuild.cpp - when to rebuild thei background index -----===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "index/BackgroundRebuild.h"
#include "Compiler.h"
#include "Headers.h"
#include "Logger.h"
#include "ParsedAST.h"
#include "Path.h"
#include "SourceCode.h"
#include "Symbol.h"
#include "Threading.h"
#include "Trace.h"
#include "URI.h"
#include "index/FileIndex.h"
#include "index/IndexAction.h"
#include "index/MemIndex.h"
#include "index/Ref.h"
#include "index/Relation.h"
#include "index/Serialization.h"
#include "index/SymbolCollector.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/ScopeExit.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/Threading.h"
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <memory>
#include <mutex>
#include <numeric>
#include <queue>
#include <random>
#include <string>
#include <thread>
namespace clang {
namespace clangd {
bool BackgroundIndexRebuilder::enoughTUsToRebuild() const {
if (!ActiveVersion) // never built
return IndexedTUs == TUsBeforeFirstBuild; // use low threshold
// rebuild if we've reached the (higher) threshold
return IndexedTUs >= IndexedTUsAtLastRebuild + TUsBeforeRebuild;
}
void BackgroundIndexRebuilder::indexedTU() {
maybeRebuild("after indexing enough files", [this] {
++IndexedTUs;
if (Loading)
return false; // rebuild once loading finishes
if (ActiveVersion != StartedVersion) // currently building
return false; // no urgency, avoid overlapping builds
return enoughTUsToRebuild();
});
}
void BackgroundIndexRebuilder::idle() {
maybeRebuild("when background indexer is idle", [this] {
// rebuild if there's anything new in the index.
// (even if currently rebuilding! this ensures eventual completeness)
return IndexedTUs > IndexedTUsAtLastRebuild;
});
}
void BackgroundIndexRebuilder::startLoading() {
std::lock_guard<std::mutex> Lock(Mu);
if (!Loading)
LoadedShards = 0;
++Loading;
}
void BackgroundIndexRebuilder::loadedShard(size_t ShardCount) {
std::lock_guard<std::mutex> Lock(Mu);
assert(Loading);
LoadedShards += ShardCount;
}
void BackgroundIndexRebuilder::doneLoading() {
maybeRebuild("after loading index from disk", [this] {
assert(Loading);
--Loading;
if (Loading) // was loading multiple batches concurrently
return false; // rebuild once the last batch is done.
// Rebuild if we loaded any shards, or if we stopped an indexedTU rebuild.
return LoadedShards > 0 || enoughTUsToRebuild();
});
}
void BackgroundIndexRebuilder::shutdown() {
std::lock_guard<std::mutex> Lock(Mu);
ShouldStop = true;
}
void BackgroundIndexRebuilder::maybeRebuild(const char *Reason,
std::function<bool()> Check) {
unsigned BuildVersion = 0;
{
std::lock_guard<std::mutex> Lock(Mu);
if (!ShouldStop && Check()) {
BuildVersion = ++StartedVersion;
IndexedTUsAtLastRebuild = IndexedTUs;
}
}
if (BuildVersion) {
std::unique_ptr<SymbolIndex> NewIndex;
{
vlog("BackgroundIndex: building version {0} {1}", BuildVersion, Reason);
trace::Span Tracer("RebuildBackgroundIndex");
SPAN_ATTACH(Tracer, "reason", Reason);
NewIndex = Source->buildIndex(IndexType::Heavy, DuplicateHandling::Merge);
}
{
std::lock_guard<std::mutex> Lock(Mu);
// Guard against rebuild finishing in the wrong order.
if (BuildVersion > ActiveVersion) {
ActiveVersion = BuildVersion;
vlog("BackgroundIndex: serving version {0} ({1} bytes)", BuildVersion,
NewIndex->estimateMemoryUsage());
Target->reset(std::move(NewIndex));
}
}
}
}
} // namespace clangd
} // namespace clang