61
61
#include " llvm/Support/Threading.h"
62
62
#include < algorithm>
63
63
#include < memory>
64
+ #include < mutex>
64
65
#include < queue>
65
66
#include < thread>
66
67
@@ -164,7 +165,7 @@ class ASTWorker {
164
165
friend class ASTWorkerHandle ;
165
166
ASTWorker (PathRef FileName, const GlobalCompilationDatabase &CDB,
166
167
TUScheduler::ASTCache &LRUCache, Semaphore &Barrier, bool RunSync,
167
- steady_clock::duration UpdateDebounce, bool StorePreamblesInMemory,
168
+ DebouncePolicy UpdateDebounce, bool StorePreamblesInMemory,
168
169
ParsingCallbacks &Callbacks);
169
170
170
171
public:
@@ -176,7 +177,7 @@ class ASTWorker {
176
177
static ASTWorkerHandle
177
178
create (PathRef FileName, const GlobalCompilationDatabase &CDB,
178
179
TUScheduler::ASTCache &IdleASTs, AsyncTaskRunner *Tasks,
179
- Semaphore &Barrier, steady_clock::duration UpdateDebounce,
180
+ Semaphore &Barrier, DebouncePolicy UpdateDebounce,
180
181
bool StorePreamblesInMemory, ParsingCallbacks &Callbacks);
181
182
~ASTWorker ();
182
183
@@ -242,7 +243,7 @@ class ASTWorker {
242
243
TUScheduler::ASTCache &IdleASTs;
243
244
const bool RunSync;
244
245
// / Time to wait after an update to see whether another update obsoletes it.
245
- const steady_clock::duration UpdateDebounce;
246
+ const DebouncePolicy UpdateDebounce;
246
247
// / File that ASTWorker is responsible for.
247
248
const Path FileName;
248
249
const GlobalCompilationDatabase &CDB;
@@ -263,6 +264,9 @@ class ASTWorker {
263
264
// / be consumed by clients of ASTWorker.
264
265
std::shared_ptr<const ParseInputs> FileInputs; /* GUARDED_BY(Mutex) */
265
266
std::shared_ptr<const PreambleData> LastBuiltPreamble; /* GUARDED_BY(Mutex) */
267
+ // / Times of recent AST rebuilds, used for UpdateDebounce computation.
268
+ llvm::SmallVector<DebouncePolicy::clock::duration, 8 >
269
+ RebuildTimes; /* GUARDED_BY(Mutex) */
266
270
// / Becomes ready when the first preamble build finishes.
267
271
Notification PreambleWasBuilt;
268
272
// / Set to true to signal run() to finish processing.
@@ -326,7 +330,7 @@ class ASTWorkerHandle {
326
330
ASTWorkerHandle
327
331
ASTWorker::create (PathRef FileName, const GlobalCompilationDatabase &CDB,
328
332
TUScheduler::ASTCache &IdleASTs, AsyncTaskRunner *Tasks,
329
- Semaphore &Barrier, steady_clock::duration UpdateDebounce,
333
+ Semaphore &Barrier, DebouncePolicy UpdateDebounce,
330
334
bool StorePreamblesInMemory, ParsingCallbacks &Callbacks) {
331
335
std::shared_ptr<ASTWorker> Worker (
332
336
new ASTWorker (FileName, CDB, IdleASTs, Barrier, /* RunSync=*/ !Tasks,
@@ -340,7 +344,7 @@ ASTWorker::create(PathRef FileName, const GlobalCompilationDatabase &CDB,
340
344
341
345
ASTWorker::ASTWorker (PathRef FileName, const GlobalCompilationDatabase &CDB,
342
346
TUScheduler::ASTCache &LRUCache, Semaphore &Barrier,
343
- bool RunSync, steady_clock::duration UpdateDebounce,
347
+ bool RunSync, DebouncePolicy UpdateDebounce,
344
348
bool StorePreamblesInMemory, ParsingCallbacks &Callbacks)
345
349
: IdleASTs(LRUCache), RunSync(RunSync), UpdateDebounce(UpdateDebounce),
346
350
FileName (FileName), CDB(CDB),
@@ -488,6 +492,7 @@ void ASTWorker::update(ParseInputs Inputs, WantDiagnostics WantDiags) {
488
492
489
493
// Get the AST for diagnostics.
490
494
llvm::Optional<std::unique_ptr<ParsedAST>> AST = IdleASTs.take (this );
495
+ auto RebuildStartTime = DebouncePolicy::clock::now ();
491
496
if (!AST) {
492
497
llvm::Optional<ParsedAST> NewAST =
493
498
buildAST (FileName, std::move (Invocation), CompilerInvocationDiags,
@@ -510,6 +515,18 @@ void ASTWorker::update(ParseInputs Inputs, WantDiagnostics WantDiags) {
510
515
// spam us with updates.
511
516
// Note *AST can still be null if buildAST fails.
512
517
if (*AST) {
518
+ {
519
+ // Try to record the AST-build time, to inform future update debouncing.
520
+ // This is best-effort only: if the lock is held, don't bother.
521
+ auto RebuildDuration = DebouncePolicy::clock::now () - RebuildStartTime;
522
+ std::unique_lock<std::mutex> Lock (Mutex, std::try_to_lock);
523
+ if (Lock.owns_lock ()) {
524
+ // Do not let RebuildTimes grow beyond its small-size (i.e. capacity).
525
+ if (RebuildTimes.size () == RebuildTimes.capacity ())
526
+ RebuildTimes.erase (RebuildTimes.begin ());
527
+ RebuildTimes.push_back (RebuildDuration);
528
+ }
529
+ }
513
530
trace::Span Span (" Running main AST callback" );
514
531
515
532
Callbacks.onMainAST (FileName, **AST, RunPublish);
@@ -750,13 +767,13 @@ Deadline ASTWorker::scheduleLocked() {
750
767
assert (!Requests.empty () && " skipped the whole queue" );
751
768
// Some updates aren't dead yet, but never end up being used.
752
769
// e.g. the first keystroke is live until obsoleted by the second.
753
- // We debounce "maybe-unused" writes, sleeping 500ms in case they become dead.
770
+ // We debounce "maybe-unused" writes, sleeping in case they become dead.
754
771
// But don't delay reads (including updates where diagnostics are needed).
755
772
for (const auto &R : Requests)
756
773
if (R.UpdateType == None || R.UpdateType == WantDiagnostics::Yes)
757
774
return Deadline::zero ();
758
775
// Front request needs to be debounced, so determine when we're ready.
759
- Deadline D (Requests.front ().AddTime + UpdateDebounce);
776
+ Deadline D (Requests.front ().AddTime + UpdateDebounce. compute (RebuildTimes) );
760
777
return D;
761
778
}
762
779
@@ -1036,5 +1053,33 @@ std::vector<Path> TUScheduler::getFilesWithCachedAST() const {
1036
1053
return Result;
1037
1054
}
1038
1055
1056
+ DebouncePolicy::clock::duration
1057
+ DebouncePolicy::compute (llvm::ArrayRef<clock::duration> History) const {
1058
+ assert (Min <= Max && " Invalid policy" );
1059
+ if (History.empty ())
1060
+ return Max; // Arbitrary.
1061
+
1062
+ // Base the result on the median rebuild.
1063
+ // nth_element needs a mutable array, take the chance to bound the data size.
1064
+ History = History.take_back (15 );
1065
+ llvm::SmallVector<clock::duration, 15 > Recent (History.begin (), History.end ());
1066
+ auto Median = Recent.begin () + Recent.size () / 2 ;
1067
+ std::nth_element (Recent.begin (), Median, Recent.end ());
1068
+
1069
+ clock::duration Target =
1070
+ std::chrono::duration_cast<clock::duration>(RebuildRatio * *Median);
1071
+ if (Target > Max)
1072
+ return Max;
1073
+ if (Target < Min)
1074
+ return Min;
1075
+ return Target;
1076
+ }
1077
+
1078
+ DebouncePolicy DebouncePolicy::fixed (clock::duration T) {
1079
+ DebouncePolicy P;
1080
+ P.Min = P.Max = T;
1081
+ return P;
1082
+ }
1083
+
1039
1084
} // namespace clangd
1040
1085
} // namespace clang
0 commit comments