1 //===-- BackgroundQueue.cpp - Task queue for background index -------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "index/Background.h"
10 #include "support/Logger.h"
15 static std::atomic
<bool> PreventStarvation
= {false};
17 void BackgroundQueue::preventThreadStarvationInTests() {
18 PreventStarvation
.store(true);
21 void BackgroundQueue::work(std::function
<void()> OnIdle
) {
23 llvm::Optional
<Task
> Task
;
25 std::unique_lock
<std::mutex
> Lock(Mu
);
26 CV
.wait(Lock
, [&] { return ShouldStop
|| !Queue
.empty(); });
33 std::pop_heap(Queue
.begin(), Queue
.end());
34 Task
= std::move(Queue
.back());
39 if (Task
->ThreadPri
!= llvm::ThreadPriority::Default
&&
40 !PreventStarvation
.load())
41 llvm::set_thread_priority(Task
->ThreadPri
);
43 if (Task
->ThreadPri
!= llvm::ThreadPriority::Default
)
44 llvm::set_thread_priority(llvm::ThreadPriority::Default
);
47 std::unique_lock
<std::mutex
> Lock(Mu
);
49 if (Stat
.Active
== 1 && Queue
.empty()) {
50 // We just finished the last item, the queue is going idle.
51 assert(ShouldStop
|| Stat
.Completed
== Stat
.Enqueued
);
52 Stat
.LastIdle
= Stat
.Completed
;
59 assert(Stat
.Active
> 0 && "before decrementing");
67 void BackgroundQueue::stop() {
69 std::lock_guard
<std::mutex
> QueueLock(Mu
);
75 // Tweaks the priority of a newly-enqueued task, or returns false to cancel it.
76 bool BackgroundQueue::adjust(Task
&T
) {
77 // It is tempting to drop duplicates of queued tasks, and merely deprioritize
78 // duplicates of completed tasks (i.e. reindexing on CDB changes). But:
79 // - the background indexer doesn't support reindexing well, e.g. staleness
80 // is checked at *enqueue* time only, and doesn't account for compile flags
81 // - reindexing on compile flags is often a poor use of CPU in practice
82 if (T
.Key
&& !SeenKeys
.insert(T
.Key
).second
)
84 T
.QueuePri
= std::max(T
.QueuePri
, Boosts
.lookup(T
.Tag
));
88 void BackgroundQueue::push(Task T
) {
90 std::lock_guard
<std::mutex
> Lock(Mu
);
93 Queue
.push_back(std::move(T
));
94 std::push_heap(Queue
.begin(), Queue
.end());
101 void BackgroundQueue::append(std::vector
<Task
> Tasks
) {
103 std::lock_guard
<std::mutex
> Lock(Mu
);
104 for (Task
&T
: Tasks
) {
107 Queue
.push_back(std::move(T
));
110 std::make_heap(Queue
.begin(), Queue
.end());
116 void BackgroundQueue::boost(llvm::StringRef Tag
, unsigned NewPriority
) {
117 std::lock_guard
<std::mutex
> Lock(Mu
);
118 unsigned &Boost
= Boosts
[Tag
];
119 bool Increase
= NewPriority
> Boost
;
122 return; // existing tasks unaffected
124 unsigned Changes
= 0;
125 for (Task
&T
: Queue
)
126 if (Tag
== T
.Tag
&& NewPriority
> T
.QueuePri
) {
127 T
.QueuePri
= NewPriority
;
131 std::make_heap(Queue
.begin(), Queue
.end());
132 // No need to signal, only rearranged items in the queue.
135 bool BackgroundQueue::blockUntilIdleForTest(
136 llvm::Optional
<double> TimeoutSeconds
) {
137 std::unique_lock
<std::mutex
> Lock(Mu
);
138 return wait(Lock
, CV
, timeoutSeconds(TimeoutSeconds
),
139 [&] { return Queue
.empty() && Stat
.Active
== 0; });
142 void BackgroundQueue::notifyProgress() const {
143 dlog("Queue: {0}/{1} ({2} active). Last idle at {3}", Stat
.Completed
,
144 Stat
.Enqueued
, Stat
.Active
, Stat
.LastIdle
);
149 } // namespace clangd