1 //===-- BackgroundRebuild.cpp - when to rebuild the background index ------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "index/BackgroundRebuild.h" 10 #include "index/FileIndex.h" 11 #include "support/Logger.h" 12 #include "support/Trace.h" 13 14 #include <atomic> 15 #include <chrono> 16 #include <condition_variable> 17 #include <memory> 18 #include <mutex> 19 #include <numeric> 20 #include <queue> 21 #include <random> 22 #include <string> 23 #include <thread> 24 25 namespace clang { 26 namespace clangd { 27 28 bool BackgroundIndexRebuilder::enoughTUsToRebuild() const { 29 if (!ActiveVersion) // never built 30 return IndexedTUs == TUsBeforeFirstBuild; // use low threshold 31 // rebuild if we've reached the (higher) threshold 32 return IndexedTUs >= IndexedTUsAtLastRebuild + TUsBeforeRebuild; 33 } 34 35 void BackgroundIndexRebuilder::indexedTU() { 36 maybeRebuild("after indexing enough files", [this] { 37 ++IndexedTUs; 38 if (Loading) 39 return false; // rebuild once loading finishes 40 if (ActiveVersion != StartedVersion) // currently building 41 return false; // no urgency, avoid overlapping builds 42 return enoughTUsToRebuild(); 43 }); 44 } 45 46 void BackgroundIndexRebuilder::idle() { 47 maybeRebuild("when background indexer is idle", [this] { 48 // rebuild if there's anything new in the index. 49 // (even if currently rebuilding! this ensures eventual completeness) 50 return IndexedTUs > IndexedTUsAtLastRebuild; 51 }); 52 } 53 54 void BackgroundIndexRebuilder::startLoading() { 55 std::lock_guard<std::mutex> Lock(Mu); 56 if (!Loading) 57 LoadedShards = 0; 58 ++Loading; 59 } 60 void BackgroundIndexRebuilder::loadedShard(size_t ShardCount) { 61 std::lock_guard<std::mutex> Lock(Mu); 62 assert(Loading); 63 LoadedShards += ShardCount; 64 } 65 void BackgroundIndexRebuilder::doneLoading() { 66 maybeRebuild("after loading index from disk", [this] { 67 assert(Loading); 68 --Loading; 69 if (Loading) // was loading multiple batches concurrently 70 return false; // rebuild once the last batch is done. 71 // Rebuild if we loaded any shards, or if we stopped an indexedTU rebuild. 72 return LoadedShards > 0 || enoughTUsToRebuild(); 73 }); 74 } 75 76 void BackgroundIndexRebuilder::shutdown() { 77 std::lock_guard<std::mutex> Lock(Mu); 78 ShouldStop = true; 79 } 80 81 void BackgroundIndexRebuilder::maybeRebuild(const char *Reason, 82 std::function<bool()> Check) { 83 unsigned BuildVersion = 0; 84 { 85 std::lock_guard<std::mutex> Lock(Mu); 86 if (!ShouldStop && Check()) { 87 BuildVersion = ++StartedVersion; 88 IndexedTUsAtLastRebuild = IndexedTUs; 89 } 90 } 91 if (BuildVersion) { 92 std::unique_ptr<SymbolIndex> NewIndex; 93 { 94 vlog("BackgroundIndex: building version {0} {1}", BuildVersion, Reason); 95 trace::Span Tracer("RebuildBackgroundIndex"); 96 SPAN_ATTACH(Tracer, "reason", Reason); 97 NewIndex = Source->buildIndex(IndexType::Heavy, DuplicateHandling::Merge); 98 } 99 { 100 std::lock_guard<std::mutex> Lock(Mu); 101 // Guard against rebuild finishing in the wrong order. 102 if (BuildVersion > ActiveVersion) { 103 ActiveVersion = BuildVersion; 104 vlog("BackgroundIndex: serving version {0} ({1} bytes)", BuildVersion, 105 NewIndex->estimateMemoryUsage()); 106 Target->reset(std::move(NewIndex)); 107 } 108 } 109 } 110 } 111 112 } // namespace clangd 113 } // namespace clang 114