xref: /llvm-project/clang-tools-extra/clangd/index/BackgroundRebuild.cpp (revision 0ccd18ead138e9efc56b6c16ded6c3f4df86ae91)
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