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"
16 static std::atomic
<bool> PreventStarvation
= {false};
18 void BackgroundQueue::preventThreadStarvationInTests() {
19 PreventStarvation
.store(true);
22 void BackgroundQueue::work(std::function
<void()> OnIdle
) {
24 std::optional
<Task
> Task
;
26 std::unique_lock
<std::mutex
> Lock(Mu
);
27 CV
.wait(Lock
, [&] { return ShouldStop
|| !Queue
.empty(); });
34 std::pop_heap(Queue
.begin(), Queue
.end());
35 Task
= std::move(Queue
.back());
40 if (Task
->ThreadPri
!= llvm::ThreadPriority::Default
&&
41 !PreventStarvation
.load())
42 llvm::set_thread_priority(Task
->ThreadPri
);
44 if (Task
->ThreadPri
!= llvm::ThreadPriority::Default
)
45 llvm::set_thread_priority(llvm::ThreadPriority::Default
);
48 std::unique_lock
<std::mutex
> Lock(Mu
);
50 if (Stat
.Active
== 1 && Queue
.empty()) {
51 // We just finished the last item, the queue is going idle.
52 assert(ShouldStop
|| Stat
.Completed
== Stat
.Enqueued
);
53 Stat
.LastIdle
= Stat
.Completed
;
60 assert(Stat
.Active
> 0 && "before decrementing");
68 void BackgroundQueue::stop() {
70 std::lock_guard
<std::mutex
> QueueLock(Mu
);
76 // Tweaks the priority of a newly-enqueued task, or returns false to cancel it.
77 bool BackgroundQueue::adjust(Task
&T
) {
78 // It is tempting to drop duplicates of queued tasks, and merely deprioritize
79 // duplicates of completed tasks (i.e. reindexing on CDB changes). But:
80 // - the background indexer doesn't support reindexing well, e.g. staleness
81 // is checked at *enqueue* time only, and doesn't account for compile flags
82 // - reindexing on compile flags is often a poor use of CPU in practice
83 if (T
.Key
&& !SeenKeys
.insert(T
.Key
).second
)
85 T
.QueuePri
= std::max(T
.QueuePri
, Boosts
.lookup(T
.Tag
));
89 void BackgroundQueue::push(Task T
) {
91 std::lock_guard
<std::mutex
> Lock(Mu
);
94 Queue
.push_back(std::move(T
));
95 std::push_heap(Queue
.begin(), Queue
.end());
102 void BackgroundQueue::append(std::vector
<Task
> Tasks
) {
104 std::lock_guard
<std::mutex
> Lock(Mu
);
105 for (Task
&T
: Tasks
) {
108 Queue
.push_back(std::move(T
));
111 std::make_heap(Queue
.begin(), Queue
.end());
117 void BackgroundQueue::boost(llvm::StringRef Tag
, unsigned NewPriority
) {
118 std::lock_guard
<std::mutex
> Lock(Mu
);
119 unsigned &Boost
= Boosts
[Tag
];
120 bool Increase
= NewPriority
> Boost
;
123 return; // existing tasks unaffected
125 unsigned Changes
= 0;
126 for (Task
&T
: Queue
)
127 if (Tag
== T
.Tag
&& NewPriority
> T
.QueuePri
) {
128 T
.QueuePri
= NewPriority
;
132 std::make_heap(Queue
.begin(), Queue
.end());
133 // No need to signal, only rearranged items in the queue.
136 bool BackgroundQueue::blockUntilIdleForTest(
137 std::optional
<double> TimeoutSeconds
) {
138 std::unique_lock
<std::mutex
> Lock(Mu
);
139 return wait(Lock
, CV
, timeoutSeconds(TimeoutSeconds
),
140 [&] { return Queue
.empty() && Stat
.Active
== 0; });
143 void BackgroundQueue::notifyProgress() const {
144 dlog("Queue: {0}/{1} ({2} active). Last idle at {3}", Stat
.Completed
,
145 Stat
.Enqueued
, Stat
.Active
, Stat
.LastIdle
);
150 } // namespace clangd