diff --git a/clang-tools-extra/clangd/TUScheduler.cpp b/clang-tools-extra/clangd/TUScheduler.cpp --- a/clang-tools-extra/clangd/TUScheduler.cpp +++ b/clang-tools-extra/clangd/TUScheduler.cpp @@ -276,6 +276,7 @@ /// Set to true to signal run() to finish processing. bool Done; /* GUARDED_BY(Mutex) */ std::deque Requests; /* GUARDED_BY(Mutex) */ + bool RunningTask = false; /* GUARDED_BY(Mutex) */ mutable std::condition_variable RequestsCV; /// Guards the callback that publishes results of AST-related computations /// (diagnostics, highlightings) and file statuses. @@ -370,7 +371,8 @@ #ifndef NDEBUG std::lock_guard Lock(Mutex); assert(Done && "handle was not destroyed"); - assert(Requests.empty() && "unprocessed requests when destroying ASTWorker"); + assert(Requests.empty() && !RunningTask && + "unprocessed requests when destroying ASTWorker"); #endif } @@ -745,7 +747,8 @@ wait(Lock, RequestsCV, Wait); } Req = std::move(Requests.front()); - // Leave it on the queue for now, so waiters don't see an empty queue. + Requests.pop_front(); + RunningTask = true; } // unlock Mutex { @@ -763,7 +766,7 @@ bool IsEmpty = false; { std::lock_guard Lock(Mutex); - Requests.pop_front(); + RunningTask = false; IsEmpty = Requests.empty(); } if (IsEmpty) @@ -843,7 +846,8 @@ bool ASTWorker::blockUntilIdle(Deadline Timeout) const { std::unique_lock Lock(Mutex); - return wait(Lock, RequestsCV, Timeout, [&] { return Requests.empty(); }); + return wait(Lock, RequestsCV, Timeout, + [&] { return Requests.empty() && !RunningTask; }); } // Render a TUAction to a user-facing string representation. diff --git a/clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp b/clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp --- a/clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp +++ b/clang-tools-extra/clangd/unittests/TUSchedulerTests.cpp @@ -373,10 +373,17 @@ std::atomic Builds(0), Actions(0); Notification Start; - updateWithDiags(S, Path, "a", WantDiagnostics::Yes, [&](std::vector) { - ++Builds; - Start.wait(); - }); + updateWithDiags(S, Path, "b", WantDiagnostics::Auto, + [&](std::vector) { ++Builds; }); + ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10))); + S.runWithAST( + "invalidatable-but-running", Path, + [&](llvm::Expected AST) { + Start.wait(); + ++Actions; + EXPECT_TRUE(bool(AST)) << "Shouldn't be invalidated, because running."; + }, + TUScheduler::InvalidateOnUpdate); S.runWithAST( "invalidatable", Path, [&](llvm::Expected AST) { @@ -421,7 +428,7 @@ ASSERT_TRUE(S.blockUntilIdle(timeoutSeconds(10))); EXPECT_EQ(2, Builds.load()) << "Middle build should be skipped"; - EXPECT_EQ(4, Actions.load()) << "All actions should run (some with error)"; + EXPECT_EQ(5, Actions.load()) << "All actions should run (some with error)"; } TEST_F(TUSchedulerTests, ManyUpdates) {