151 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			151 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			C++
		
	
	
	
//===-- BackgroundQueue.cpp - Task queue for background index -------------===//
 | 
						|
//
 | 
						|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 | 
						|
// See https://llvm.org/LICENSE.txt for license information.
 | 
						|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 | 
						|
//
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
 | 
						|
#include "index/Background.h"
 | 
						|
#include "support/Logger.h"
 | 
						|
 | 
						|
namespace clang {
 | 
						|
namespace clangd {
 | 
						|
 | 
						|
static std::atomic<bool> PreventStarvation = {false};
 | 
						|
 | 
						|
void BackgroundQueue::preventThreadStarvationInTests() {
 | 
						|
  PreventStarvation.store(true);
 | 
						|
}
 | 
						|
 | 
						|
void BackgroundQueue::work(std::function<void()> OnIdle) {
 | 
						|
  while (true) {
 | 
						|
    llvm::Optional<Task> Task;
 | 
						|
    {
 | 
						|
      std::unique_lock<std::mutex> Lock(Mu);
 | 
						|
      CV.wait(Lock, [&] { return ShouldStop || !Queue.empty(); });
 | 
						|
      if (ShouldStop) {
 | 
						|
        Queue.clear();
 | 
						|
        CV.notify_all();
 | 
						|
        return;
 | 
						|
      }
 | 
						|
      ++Stat.Active;
 | 
						|
      std::pop_heap(Queue.begin(), Queue.end());
 | 
						|
      Task = std::move(Queue.back());
 | 
						|
      Queue.pop_back();
 | 
						|
      notifyProgress();
 | 
						|
    }
 | 
						|
 | 
						|
    if (Task->ThreadPri != llvm::ThreadPriority::Default &&
 | 
						|
        !PreventStarvation.load())
 | 
						|
      llvm::set_thread_priority(Task->ThreadPri);
 | 
						|
    Task->Run();
 | 
						|
    if (Task->ThreadPri != llvm::ThreadPriority::Default)
 | 
						|
      llvm::set_thread_priority(llvm::ThreadPriority::Default);
 | 
						|
 | 
						|
    {
 | 
						|
      std::unique_lock<std::mutex> Lock(Mu);
 | 
						|
      ++Stat.Completed;
 | 
						|
      if (Stat.Active == 1 && Queue.empty()) {
 | 
						|
        // We just finished the last item, the queue is going idle.
 | 
						|
        assert(ShouldStop || Stat.Completed == Stat.Enqueued);
 | 
						|
        Stat.LastIdle = Stat.Completed;
 | 
						|
        if (OnIdle) {
 | 
						|
          Lock.unlock();
 | 
						|
          OnIdle();
 | 
						|
          Lock.lock();
 | 
						|
        }
 | 
						|
      }
 | 
						|
      assert(Stat.Active > 0 && "before decrementing");
 | 
						|
      --Stat.Active;
 | 
						|
      notifyProgress();
 | 
						|
    }
 | 
						|
    CV.notify_all();
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void BackgroundQueue::stop() {
 | 
						|
  {
 | 
						|
    std::lock_guard<std::mutex> QueueLock(Mu);
 | 
						|
    ShouldStop = true;
 | 
						|
  }
 | 
						|
  CV.notify_all();
 | 
						|
}
 | 
						|
 | 
						|
// Tweaks the priority of a newly-enqueued task, or returns false to cancel it.
 | 
						|
bool BackgroundQueue::adjust(Task &T) {
 | 
						|
  // It is tempting to drop duplicates of queued tasks, and merely deprioritize
 | 
						|
  // duplicates of completed tasks (i.e. reindexing on CDB changes). But:
 | 
						|
  //  - the background indexer doesn't support reindexing well, e.g. staleness
 | 
						|
  //    is checked at *enqueue* time only, and doesn't account for compile flags
 | 
						|
  //  - reindexing on compile flags is often a poor use of CPU in practice
 | 
						|
  if (T.Key && !SeenKeys.insert(T.Key).second)
 | 
						|
    return false;
 | 
						|
  T.QueuePri = std::max(T.QueuePri, Boosts.lookup(T.Tag));
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
void BackgroundQueue::push(Task T) {
 | 
						|
  {
 | 
						|
    std::lock_guard<std::mutex> Lock(Mu);
 | 
						|
    if (!adjust(T))
 | 
						|
      return;
 | 
						|
    Queue.push_back(std::move(T));
 | 
						|
    std::push_heap(Queue.begin(), Queue.end());
 | 
						|
    ++Stat.Enqueued;
 | 
						|
    notifyProgress();
 | 
						|
  }
 | 
						|
  CV.notify_all();
 | 
						|
}
 | 
						|
 | 
						|
void BackgroundQueue::append(std::vector<Task> Tasks) {
 | 
						|
  {
 | 
						|
    std::lock_guard<std::mutex> Lock(Mu);
 | 
						|
    for (Task &T : Tasks) {
 | 
						|
      if (!adjust(T))
 | 
						|
        continue;
 | 
						|
      Queue.push_back(std::move(T));
 | 
						|
      ++Stat.Enqueued;
 | 
						|
    }
 | 
						|
    std::make_heap(Queue.begin(), Queue.end());
 | 
						|
    notifyProgress();
 | 
						|
  }
 | 
						|
  CV.notify_all();
 | 
						|
}
 | 
						|
 | 
						|
void BackgroundQueue::boost(llvm::StringRef Tag, unsigned NewPriority) {
 | 
						|
  std::lock_guard<std::mutex> Lock(Mu);
 | 
						|
  unsigned &Boost = Boosts[Tag];
 | 
						|
  bool Increase = NewPriority > Boost;
 | 
						|
  Boost = NewPriority;
 | 
						|
  if (!Increase)
 | 
						|
    return; // existing tasks unaffected
 | 
						|
 | 
						|
  unsigned Changes = 0;
 | 
						|
  for (Task &T : Queue)
 | 
						|
    if (Tag == T.Tag && NewPriority > T.QueuePri) {
 | 
						|
      T.QueuePri = NewPriority;
 | 
						|
      ++Changes;
 | 
						|
    }
 | 
						|
  if (Changes)
 | 
						|
    std::make_heap(Queue.begin(), Queue.end());
 | 
						|
  // No need to signal, only rearranged items in the queue.
 | 
						|
}
 | 
						|
 | 
						|
bool BackgroundQueue::blockUntilIdleForTest(
 | 
						|
    llvm::Optional<double> TimeoutSeconds) {
 | 
						|
  std::unique_lock<std::mutex> Lock(Mu);
 | 
						|
  return wait(Lock, CV, timeoutSeconds(TimeoutSeconds),
 | 
						|
              [&] { return Queue.empty() && Stat.Active == 0; });
 | 
						|
}
 | 
						|
 | 
						|
void BackgroundQueue::notifyProgress() const {
 | 
						|
  dlog("Queue: {0}/{1} ({2} active). Last idle at {3}", Stat.Completed,
 | 
						|
       Stat.Enqueued, Stat.Active, Stat.LastIdle);
 | 
						|
  if (OnProgress)
 | 
						|
    OnProgress(Stat);
 | 
						|
}
 | 
						|
 | 
						|
} // namespace clangd
 | 
						|
} // namespace clang
 |