<div dir="ltr">The TUSchedulerTest::Debounce breaks on Windows buildbots. Probably because the timeouts of 50ms/10ms/40ms are too low to be scheduled properly.<div>Increased the timeouts in r326598 to 1s/200ms/2s, hopefully that would unbreak the buildbots.</div><div><br></div><div>We could tweak them back to lower values that work on Monday :-)<br></div></div><br><br><div class="gmail_quote"><div dir="ltr">On Fri, Mar 2, 2018 at 9:58 AM Sam McCall via cfe-commits <<a href="mailto:cfe-commits@lists.llvm.org">cfe-commits@lists.llvm.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Author: sammccall<br>
Date: Fri Mar 2 00:56:37 2018<br>
New Revision: 326546<br>
<br>
URL: <a href="http://llvm.org/viewvc/llvm-project?rev=326546&view=rev" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project?rev=326546&view=rev</a><br>
Log:<br>
[clangd] Debounce streams of updates.<br>
<br>
Summary:<br>
Don't actually start building ASTs for new revisions until either:<br>
- 500ms have passed since the last revision, or<br>
- we actually need the revision for something (or to unblock the queue)<br>
<br>
In practice, this avoids the "first keystroke results in diagnostics" problem.<br>
This is kind of awkward to test, and the test is pretty bad.<br>
It can be observed nicely by capturing a trace, though.<br>
<br>
Reviewers: hokein, ilya-biryukov<br>
<br>
Subscribers: klimek, jkorous-apple, ioeric, cfe-commits<br>
<br>
Differential Revision: <a href="https://reviews.llvm.org/D43648" rel="noreferrer" target="_blank">https://reviews.llvm.org/D43648</a><br>
<br>
Modified:<br>
clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp<br>
clang-tools-extra/trunk/clangd/ClangdServer.cpp<br>
clang-tools-extra/trunk/clangd/ClangdServer.h<br>
clang-tools-extra/trunk/clangd/TUScheduler.cpp<br>
clang-tools-extra/trunk/clangd/TUScheduler.h<br>
clang-tools-extra/trunk/clangd/Threading.cpp<br>
clang-tools-extra/trunk/clangd/Threading.h<br>
clang-tools-extra/trunk/unittests/clangd/TUSchedulerTests.cpp<br>
<br>
Modified: clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp?rev=326546&r1=326545&r2=326546&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp?rev=326546&r1=326545&r2=326546&view=diff</a><br>
==============================================================================<br>
--- clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp (original)<br>
+++ clang-tools-extra/trunk/clangd/ClangdLSPServer.cpp Fri Mar 2 00:56:37 2018<br>
@@ -405,7 +405,7 @@ ClangdLSPServer::ClangdLSPServer(JSONOut<br>
: Out(Out), CDB(std::move(CompileCommandsDir)), CCOpts(CCOpts),<br>
Server(CDB, /*DiagConsumer=*/*this, FSProvider, AsyncThreadsCount,<br>
StorePreamblesInMemory, BuildDynamicSymbolIndex, StaticIdx,<br>
- ResourceDir) {}<br>
+ ResourceDir, /*UpdateDebounce=*/std::chrono::milliseconds(500)) {}<br>
<br>
bool ClangdLSPServer::run(std::istream &In, JSONStreamStyle InputStyle) {<br>
assert(!IsDone && "Run was called before");<br>
<br>
Modified: clang-tools-extra/trunk/clangd/ClangdServer.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdServer.cpp?rev=326546&r1=326545&r2=326546&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdServer.cpp?rev=326546&r1=326545&r2=326546&view=diff</a><br>
==============================================================================<br>
--- clang-tools-extra/trunk/clangd/ClangdServer.cpp (original)<br>
+++ clang-tools-extra/trunk/clangd/ClangdServer.cpp Fri Mar 2 00:56:37 2018<br>
@@ -76,7 +76,8 @@ ClangdServer::ClangdServer(GlobalCompila<br>
unsigned AsyncThreadsCount,<br>
bool StorePreamblesInMemory,<br>
bool BuildDynamicSymbolIndex, SymbolIndex *StaticIdx,<br>
- llvm::Optional<StringRef> ResourceDir)<br>
+ llvm::Optional<StringRef> ResourceDir,<br>
+ std::chrono::steady_clock::duration UpdateDebounce)<br>
: CompileArgs(CDB,<br>
ResourceDir ? ResourceDir->str() : getStandardResourceDir()),<br>
DiagConsumer(DiagConsumer), FSProvider(FSProvider),<br>
@@ -91,7 +92,8 @@ ClangdServer::ClangdServer(GlobalCompila<br>
FileIdx<br>
? [this](PathRef Path,<br>
ParsedAST *AST) { FileIdx->update(Path, AST); }<br>
- : ASTParsedCallback()) {<br>
+ : ASTParsedCallback(),<br>
+ UpdateDebounce) {<br>
if (FileIdx && StaticIdx) {<br>
MergedIndex = mergeIndex(FileIdx.get(), StaticIdx);<br>
Index = MergedIndex.get();<br>
<br>
Modified: clang-tools-extra/trunk/clangd/ClangdServer.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdServer.h?rev=326546&r1=326545&r2=326546&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/ClangdServer.h?rev=326546&r1=326545&r2=326546&view=diff</a><br>
==============================================================================<br>
--- clang-tools-extra/trunk/clangd/ClangdServer.h (original)<br>
+++ clang-tools-extra/trunk/clangd/ClangdServer.h Fri Mar 2 00:56:37 2018<br>
@@ -125,6 +125,8 @@ public:<br>
/// \p DiagConsumer. Note that a callback to \p DiagConsumer happens on a<br>
/// worker thread. Therefore, instances of \p DiagConsumer must properly<br>
/// synchronize access to shared state.<br>
+ /// UpdateDebounce determines how long to wait after a new version of the file<br>
+ /// before starting to compute diagnostics.<br>
///<br>
/// \p StorePreamblesInMemory defines whether the Preambles generated by<br>
/// clangd are stored in-memory or on disk.<br>
@@ -135,13 +137,17 @@ public:<br>
///<br>
/// If \p StaticIdx is set, ClangdServer uses the index for global code<br>
/// completion.<br>
+ /// FIXME(sammccall): pull out an options struct.<br>
ClangdServer(GlobalCompilationDatabase &CDB,<br>
DiagnosticsConsumer &DiagConsumer,<br>
FileSystemProvider &FSProvider, unsigned AsyncThreadsCount,<br>
bool StorePreamblesInMemory,<br>
bool BuildDynamicSymbolIndex = false,<br>
SymbolIndex *StaticIdx = nullptr,<br>
- llvm::Optional<StringRef> ResourceDir = llvm::None);<br>
+ llvm::Optional<StringRef> ResourceDir = llvm::None,<br>
+ /* Tiny default debounce, so tests hit the debounce logic */<br>
+ std::chrono::steady_clock::duration UpdateDebounce =<br>
+ std::chrono::milliseconds(20));<br>
<br>
/// Set the root path of the workspace.<br>
void setRootPath(PathRef RootPath);<br>
<br>
Modified: clang-tools-extra/trunk/clangd/TUScheduler.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/TUScheduler.cpp?rev=326546&r1=326545&r2=326546&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/TUScheduler.cpp?rev=326546&r1=326545&r2=326546&view=diff</a><br>
==============================================================================<br>
--- clang-tools-extra/trunk/clangd/TUScheduler.cpp (original)<br>
+++ clang-tools-extra/trunk/clangd/TUScheduler.cpp Fri Mar 2 00:56:37 2018<br>
@@ -54,6 +54,7 @@<br>
<br>
namespace clang {<br>
namespace clangd {<br>
+using std::chrono::steady_clock;<br>
namespace {<br>
class ASTWorkerHandle;<br>
<br>
@@ -69,8 +70,8 @@ class ASTWorkerHandle;<br>
/// worker.<br>
class ASTWorker {<br>
friend class ASTWorkerHandle;<br>
- ASTWorker(llvm::StringRef File, Semaphore &Barrier, CppFile AST,<br>
- bool RunSync);<br>
+ ASTWorker(llvm::StringRef File, Semaphore &Barrier, CppFile AST, bool RunSync,<br>
+ steady_clock::duration UpdateDebounce);<br>
<br>
public:<br>
/// Create a new ASTWorker and return a handle to it.<br>
@@ -79,7 +80,8 @@ public:<br>
/// synchronously instead. \p Barrier is acquired when processing each<br>
/// request, it is be used to limit the number of actively running threads.<br>
static ASTWorkerHandle Create(llvm::StringRef File, AsyncTaskRunner *Tasks,<br>
- Semaphore &Barrier, CppFile AST);<br>
+ Semaphore &Barrier, CppFile AST,<br>
+ steady_clock::duration UpdateDebounce);<br>
~ASTWorker();<br>
<br>
void update(ParseInputs Inputs, WantDiagnostics,<br>
@@ -101,18 +103,27 @@ private:<br>
/// Adds a new task to the end of the request queue.<br>
void startTask(llvm::StringRef Name, UniqueFunction<void()> Task,<br>
llvm::Optional<WantDiagnostics> UpdateType);<br>
+ /// Determines the next action to perform.<br>
+ /// All actions that should never run are disarded.<br>
+ /// Returns a deadline for the next action. If it's expired, run now.<br>
+ /// scheduleLocked() is called again at the deadline, or if requests arrive.<br>
+ Deadline scheduleLocked();<br>
/// Should the first task in the queue be skipped instead of run?<br>
bool shouldSkipHeadLocked() const;<br>
<br>
struct Request {<br>
UniqueFunction<void()> Action;<br>
std::string Name;<br>
+ steady_clock::time_point AddTime;<br>
Context Ctx;<br>
llvm::Optional<WantDiagnostics> UpdateType;<br>
};<br>
<br>
- std::string File;<br>
+ const std::string File;<br>
const bool RunSync;<br>
+ // Time to wait after an update to see whether another update obsoletes it.<br>
+ const steady_clock::duration UpdateDebounce;<br>
+<br>
Semaphore &Barrier;<br>
// AST and FileInputs are only accessed on the processing thread from run().<br>
CppFile AST;<br>
@@ -172,9 +183,10 @@ private:<br>
};<br>
<br>
ASTWorkerHandle ASTWorker::Create(llvm::StringRef File, AsyncTaskRunner *Tasks,<br>
- Semaphore &Barrier, CppFile AST) {<br>
- std::shared_ptr<ASTWorker> Worker(<br>
- new ASTWorker(File, Barrier, std::move(AST), /*RunSync=*/!Tasks));<br>
+ Semaphore &Barrier, CppFile AST,<br>
+ steady_clock::duration UpdateDebounce) {<br>
+ std::shared_ptr<ASTWorker> Worker(new ASTWorker(<br>
+ File, Barrier, std::move(AST), /*RunSync=*/!Tasks, UpdateDebounce));<br>
if (Tasks)<br>
Tasks->runAsync("worker:" + llvm::sys::path::filename(File),<br>
[Worker]() { Worker->run(); });<br>
@@ -183,9 +195,9 @@ ASTWorkerHandle ASTWorker::Create(llvm::<br>
}<br>
<br>
ASTWorker::ASTWorker(llvm::StringRef File, Semaphore &Barrier, CppFile AST,<br>
- bool RunSync)<br>
- : File(File), RunSync(RunSync), Barrier(Barrier), AST(std::move(AST)),<br>
- Done(false) {<br>
+ bool RunSync, steady_clock::duration UpdateDebounce)<br>
+ : File(File), RunSync(RunSync), UpdateDebounce(UpdateDebounce),<br>
+ Barrier(Barrier), AST(std::move(AST)), Done(false) {<br>
if (RunSync)<br>
return;<br>
}<br>
@@ -275,8 +287,8 @@ void ASTWorker::startTask(llvm::StringRe<br>
{<br>
std::lock_guard<std::mutex> Lock(Mutex);<br>
assert(!Done && "running a task after stop()");<br>
- Requests.push_back(<br>
- {std::move(Task), Name, Context::current().clone(), UpdateType});<br>
+ Requests.push_back({std::move(Task), Name, steady_clock::now(),<br>
+ Context::current().clone(), UpdateType});<br>
}<br>
RequestsCV.notify_all();<br>
}<br>
@@ -286,17 +298,31 @@ void ASTWorker::run() {<br>
Request Req;<br>
{<br>
std::unique_lock<std::mutex> Lock(Mutex);<br>
- RequestsCV.wait(Lock, [&]() { return Done || !Requests.empty(); });<br>
- if (Requests.empty()) {<br>
- assert(Done);<br>
- return;<br>
- }<br>
- // Even when Done is true, we finish processing all pending requests<br>
- // before exiting the processing loop.<br>
+ for (auto Wait = scheduleLocked(); !Wait.expired();<br>
+ Wait = scheduleLocked()) {<br>
+ if (Done) {<br>
+ if (Requests.empty())<br>
+ return;<br>
+ else // Even though Done is set, finish pending requests.<br>
+ break; // However, skip delays to shutdown fast.<br>
+ }<br>
+<br>
+ // Tracing: we have a next request, attribute this sleep to it.<br>
+ Optional<WithContext> Ctx;<br>
+ Optional<trace::Span> Tracer;<br>
+ if (!Requests.empty()) {<br>
+ Ctx.emplace(Requests.front().Ctx.clone());<br>
+ Tracer.emplace("Debounce");<br>
+ SPAN_ATTACH(*Tracer, "next_request", Requests.front().Name);<br>
+ if (!(Wait == Deadline::infinity()))<br>
+ SPAN_ATTACH(*Tracer, "sleep_ms",<br>
+ std::chrono::duration_cast<std::chrono::milliseconds>(<br>
+ Wait.time() - steady_clock::now())<br>
+ .count());<br>
+ }<br>
<br>
- while (shouldSkipHeadLocked())<br>
- Requests.pop_front();<br>
- assert(!Requests.empty() && "skipped the whole queue");<br>
+ wait(Lock, RequestsCV, Wait);<br>
+ }<br>
Req = std::move(Requests.front());<br>
// Leave it on the queue for now, so waiters don't see an empty queue.<br>
} // unlock Mutex<br>
@@ -316,6 +342,24 @@ void ASTWorker::run() {<br>
}<br>
}<br>
<br>
+Deadline ASTWorker::scheduleLocked() {<br>
+ if (Requests.empty())<br>
+ return Deadline::infinity(); // Wait for new requests.<br>
+ while (shouldSkipHeadLocked())<br>
+ Requests.pop_front();<br>
+ assert(!Requests.empty() && "skipped the whole queue");<br>
+ // Some updates aren't dead yet, but never end up being used.<br>
+ // e.g. the first keystroke is live until obsoleted by the second.<br>
+ // We debounce "maybe-unused" writes, sleeping 500ms in case they become dead.<br>
+ // But don't delay reads (including updates where diagnostics are needed).<br>
+ for (const auto &R : Requests)<br>
+ if (R.UpdateType == None || R.UpdateType == WantDiagnostics::Yes)<br>
+ return Deadline::zero();<br>
+ // Front request needs to be debounced, so determine when we're ready.<br>
+ Deadline D(Requests.front().AddTime + UpdateDebounce);<br>
+ return D;<br>
+}<br>
+<br>
// Returns true if Requests.front() is a dead update that can be skipped.<br>
bool ASTWorker::shouldSkipHeadLocked() const {<br>
assert(!Requests.empty());<br>
@@ -370,10 +414,12 @@ struct TUScheduler::FileData {<br>
<br>
TUScheduler::TUScheduler(unsigned AsyncThreadsCount,<br>
bool StorePreamblesInMemory,<br>
- ASTParsedCallback ASTCallback)<br>
+ ASTParsedCallback ASTCallback,<br>
+ steady_clock::duration UpdateDebounce)<br>
: StorePreamblesInMemory(StorePreamblesInMemory),<br>
PCHOps(std::make_shared<PCHContainerOperations>()),<br>
- ASTCallback(std::move(ASTCallback)), Barrier(AsyncThreadsCount) {<br>
+ ASTCallback(std::move(ASTCallback)), Barrier(AsyncThreadsCount),<br>
+ UpdateDebounce(UpdateDebounce) {<br>
if (0 < AsyncThreadsCount) {<br>
PreambleTasks.emplace();<br>
WorkerThreads.emplace();<br>
@@ -409,7 +455,8 @@ void TUScheduler::update(<br>
// Create a new worker to process the AST-related tasks.<br>
ASTWorkerHandle Worker = ASTWorker::Create(<br>
File, WorkerThreads ? WorkerThreads.getPointer() : nullptr, Barrier,<br>
- CppFile(File, StorePreamblesInMemory, PCHOps, ASTCallback));<br>
+ CppFile(File, StorePreamblesInMemory, PCHOps, ASTCallback),<br>
+ UpdateDebounce);<br>
FD = std::unique_ptr<FileData>(new FileData{Inputs, std::move(Worker)});<br>
} else {<br>
FD->Inputs = Inputs;<br>
<br>
Modified: clang-tools-extra/trunk/clangd/TUScheduler.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/TUScheduler.h?rev=326546&r1=326545&r2=326546&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/TUScheduler.h?rev=326546&r1=326545&r2=326546&view=diff</a><br>
==============================================================================<br>
--- clang-tools-extra/trunk/clangd/TUScheduler.h (original)<br>
+++ clang-tools-extra/trunk/clangd/TUScheduler.h Fri Mar 2 00:56:37 2018<br>
@@ -17,6 +17,7 @@<br>
<br>
namespace clang {<br>
namespace clangd {<br>
+<br>
/// Returns a number of a default async threads to use for TUScheduler.<br>
/// Returned value is always >= 1 (i.e. will not cause requests to be processed<br>
/// synchronously).<br>
@@ -46,10 +47,12 @@ enum class WantDiagnostics {<br>
/// and scheduling tasks.<br>
/// Callbacks are run on a threadpool and it's appropriate to do slow work in<br>
/// them. Each task has a name, used for tracing (should be UpperCamelCase).<br>
+/// FIXME(sammccall): pull out a scheduler options struct.<br>
class TUScheduler {<br>
public:<br>
TUScheduler(unsigned AsyncThreadsCount, bool StorePreamblesInMemory,<br>
- ASTParsedCallback ASTCallback);<br>
+ ASTParsedCallback ASTCallback,<br>
+ std::chrono::steady_clock::duration UpdateDebounce);<br>
~TUScheduler();<br>
<br>
/// Returns estimated memory usage for each of the currently open files.<br>
@@ -101,6 +104,7 @@ private:<br>
// asynchronously.<br>
llvm::Optional<AsyncTaskRunner> PreambleTasks;<br>
llvm::Optional<AsyncTaskRunner> WorkerThreads;<br>
+ std::chrono::steady_clock::duration UpdateDebounce;<br>
};<br>
} // namespace clangd<br>
} // namespace clang<br>
<br>
Modified: clang-tools-extra/trunk/clangd/Threading.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Threading.cpp?rev=326546&r1=326545&r2=326546&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Threading.cpp?rev=326546&r1=326545&r2=326546&view=diff</a><br>
==============================================================================<br>
--- clang-tools-extra/trunk/clangd/Threading.cpp (original)<br>
+++ clang-tools-extra/trunk/clangd/Threading.cpp Fri Mar 2 00:56:37 2018<br>
@@ -76,10 +76,19 @@ void AsyncTaskRunner::runAsync(llvm::Twi<br>
Deadline timeoutSeconds(llvm::Optional<double> Seconds) {<br>
using namespace std::chrono;<br>
if (!Seconds)<br>
- return llvm::None;<br>
+ return Deadline::infinity();<br>
return steady_clock::now() +<br>
duration_cast<steady_clock::duration>(duration<double>(*Seconds));<br>
}<br>
<br>
+void wait(std::unique_lock<std::mutex> &Lock, std::condition_variable &CV,<br>
+ Deadline D) {<br>
+ if (D == Deadline::zero())<br>
+ return;<br>
+ if (D == Deadline::infinity())<br>
+ return CV.wait(Lock);<br>
+ CV.wait_until(Lock, D.time());<br>
+}<br>
+<br>
} // namespace clangd<br>
} // namespace clang<br>
<br>
Modified: clang-tools-extra/trunk/clangd/Threading.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Threading.h?rev=326546&r1=326545&r2=326546&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Threading.h?rev=326546&r1=326545&r2=326546&view=diff</a><br>
==============================================================================<br>
--- clang-tools-extra/trunk/clangd/Threading.h (original)<br>
+++ clang-tools-extra/trunk/clangd/Threading.h Fri Mar 2 00:56:37 2018<br>
@@ -50,18 +50,50 @@ private:<br>
std::size_t FreeSlots;<br>
};<br>
<br>
-/// A point in time we may wait for, or None to wait forever.<br>
+/// A point in time we can wait for.<br>
+/// Can be zero (don't wait) or infinity (wait forever).<br>
/// (Not time_point::max(), because many std::chrono implementations overflow).<br>
-using Deadline = llvm::Optional<std::chrono::steady_clock::time_point>;<br>
-/// Makes a deadline from a timeout in seconds.<br>
+class Deadline {<br>
+public:<br>
+ Deadline(std::chrono::steady_clock::time_point Time)<br>
+ : Type(Finite), Time(Time) {}<br>
+ static Deadline zero() { return Deadline(Zero); }<br>
+ static Deadline infinity() { return Deadline(Infinite); }<br>
+<br>
+ std::chrono::steady_clock::time_point time() const {<br>
+ assert(Type == Finite);<br>
+ return Time;<br>
+ }<br>
+ bool expired() const {<br>
+ return (Type == Zero) ||<br>
+ (Type == Finite && Time < std::chrono::steady_clock::now());<br>
+ }<br>
+ bool operator==(const Deadline &Other) const {<br>
+ return (Type == Other.Type) && (Type != Finite || Time == Other.Time);<br>
+ }<br>
+<br>
+private:<br>
+ enum Type { Zero, Infinite, Finite };<br>
+<br>
+ Deadline(enum Type Type) : Type(Type) {}<br>
+ enum Type Type;<br>
+ std::chrono::steady_clock::time_point Time;<br>
+};<br>
+<br>
+/// Makes a deadline from a timeout in seconds. None means wait forever.<br>
Deadline timeoutSeconds(llvm::Optional<double> Seconds);<br>
+/// Wait once on CV for the specified duration.<br>
+void wait(std::unique_lock<std::mutex> &Lock, std::condition_variable &CV,<br>
+ Deadline D);<br>
/// Waits on a condition variable until F() is true or D expires.<br>
template <typename Func><br>
LLVM_NODISCARD bool wait(std::unique_lock<std::mutex> &Lock,<br>
std::condition_variable &CV, Deadline D, Func F) {<br>
- if (D)<br>
- return CV.wait_until(Lock, *D, F);<br>
- CV.wait(Lock, F);<br>
+ while (!F()) {<br>
+ if (D.expired())<br>
+ return false;<br>
+ wait(Lock, CV, D);<br>
+ }<br>
return true;<br>
}<br>
<br>
@@ -73,7 +105,7 @@ public:<br>
/// Destructor waits for all pending tasks to finish.<br>
~AsyncTaskRunner();<br>
<br>
- void wait() const { (void) wait(llvm::None); }<br>
+ void wait() const { (void)wait(Deadline::infinity()); }<br>
LLVM_NODISCARD bool wait(Deadline D) const;<br>
// The name is used for tracing and debugging (e.g. to name a spawned thread).<br>
void runAsync(llvm::Twine Name, UniqueFunction<void()> Action);<br>
<br>
Modified: clang-tools-extra/trunk/unittests/clangd/TUSchedulerTests.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/TUSchedulerTests.cpp?rev=326546&r1=326545&r2=326546&view=diff" rel="noreferrer" target="_blank">http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/TUSchedulerTests.cpp?rev=326546&r1=326545&r2=326546&view=diff</a><br>
==============================================================================<br>
--- clang-tools-extra/trunk/unittests/clangd/TUSchedulerTests.cpp (original)<br>
+++ clang-tools-extra/trunk/unittests/clangd/TUSchedulerTests.cpp Fri Mar 2 00:56:37 2018<br>
@@ -42,7 +42,8 @@ private:<br>
TEST_F(TUSchedulerTests, MissingFiles) {<br>
TUScheduler S(getDefaultAsyncThreadsCount(),<br>
/*StorePreamblesInMemory=*/true,<br>
- /*ASTParsedCallback=*/nullptr);<br>
+ /*ASTParsedCallback=*/nullptr,<br>
+ /*UpdateDebounce=*/std::chrono::steady_clock::duration::zero());<br>
<br>
auto Added = testPath("added.cpp");<br>
Files[Added] = "";<br>
@@ -94,9 +95,11 @@ TEST_F(TUSchedulerTests, WantDiagnostics<br>
// To avoid a racy test, don't allow tasks to actualy run on the worker<br>
// thread until we've scheduled them all.<br>
Notification Ready;<br>
- TUScheduler S(getDefaultAsyncThreadsCount(),<br>
- /*StorePreamblesInMemory=*/true,<br>
- /*ASTParsedCallback=*/nullptr);<br>
+ TUScheduler S(<br>
+ getDefaultAsyncThreadsCount(),<br>
+ /*StorePreamblesInMemory=*/true,<br>
+ /*ASTParsedCallback=*/nullptr,<br>
+ /*UpdateDebounce=*/std::chrono::steady_clock::duration::zero());<br>
auto Path = testPath("foo.cpp");<br>
S.update(Path, getInputs(Path, ""), WantDiagnostics::Yes,<br>
[&](std::vector<DiagWithFixIts>) { Ready.wait(); });<br>
@@ -118,6 +121,28 @@ TEST_F(TUSchedulerTests, WantDiagnostics<br>
EXPECT_EQ(2, CallbackCount);<br>
}<br>
<br>
+TEST_F(TUSchedulerTests, Debounce) {<br>
+ std::atomic<int> CallbackCount(0);<br>
+ {<br>
+ TUScheduler S(getDefaultAsyncThreadsCount(),<br>
+ /*StorePreamblesInMemory=*/true,<br>
+ /*ASTParsedCallback=*/nullptr,<br>
+ /*UpdateDebounce=*/std::chrono::milliseconds(50));<br>
+ auto Path = testPath("foo.cpp");<br>
+ S.update(Path, getInputs(Path, "auto (debounced)"), WantDiagnostics::Auto,<br>
+ [&](std::vector<DiagWithFixIts> Diags) {<br>
+ ADD_FAILURE() << "auto should have been debounced and canceled";<br>
+ });<br>
+ std::this_thread::sleep_for(std::chrono::milliseconds(10));<br>
+ S.update(Path, getInputs(Path, "auto (timed out)"), WantDiagnostics::Auto,<br>
+ [&](std::vector<DiagWithFixIts> Diags) { ++CallbackCount; });<br>
+ std::this_thread::sleep_for(std::chrono::milliseconds(60));<br>
+ S.update(Path, getInputs(Path, "auto (shut down)"), WantDiagnostics::Auto,<br>
+ [&](std::vector<DiagWithFixIts> Diags) { ++CallbackCount; });<br>
+ }<br>
+ EXPECT_EQ(2, CallbackCount);<br>
+}<br>
+<br>
TEST_F(TUSchedulerTests, ManyUpdates) {<br>
const int FilesCount = 3;<br>
const int UpdatesPerFile = 10;<br>
@@ -131,7 +156,8 @@ TEST_F(TUSchedulerTests, ManyUpdates) {<br>
{<br>
TUScheduler S(getDefaultAsyncThreadsCount(),<br>
/*StorePreamblesInMemory=*/true,<br>
- /*ASTParsedCallback=*/nullptr);<br>
+ /*ASTParsedCallback=*/nullptr,<br>
+ /*UpdateDebounce=*/std::chrono::milliseconds(50));<br>
<br>
std::vector<std::string> Files;<br>
for (int I = 0; I < FilesCount; ++I) {<br>
<br>
<br>
_______________________________________________<br>
cfe-commits mailing list<br>
<a href="mailto:cfe-commits@lists.llvm.org" target="_blank">cfe-commits@lists.llvm.org</a><br>
<a href="http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits" rel="noreferrer" target="_blank">http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits</a><br>
</blockquote></div><br clear="all"><div><br></div>-- <br><div dir="ltr" class="gmail_signature" data-smartmail="gmail_signature"><div dir="ltr"><div><div dir="ltr"><div>Regards,</div><div>Ilya Biryukov</div></div></div></div></div>