Index: clangd/Threading.h =================================================================== --- clangd/Threading.h +++ clangd/Threading.h @@ -17,6 +17,7 @@ #include #include #include +#include #include namespace clang { @@ -115,6 +116,16 @@ mutable std::condition_variable TasksReachedZero; std::size_t InFlightTasks = 0; }; + +enum class ThreadPriority { + Low = 0, + Normal = 1, +}; + +// Tries to set calling threads scheduling priority, on some platforms it might +// be a no-op. +void setThreadPriority(ThreadPriority Priority); + } // namespace clangd } // namespace clang #endif Index: clangd/Threading.cpp =================================================================== --- clangd/Threading.cpp +++ clangd/Threading.cpp @@ -1,9 +1,13 @@ #include "Threading.h" #include "Trace.h" #include "llvm/ADT/ScopeExit.h" +#include "llvm/Config/config.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/Threading.h" #include +#ifdef HAVE_PTHREAD_H +#include +#endif using namespace llvm; namespace clang { @@ -97,5 +101,15 @@ CV.wait_until(Lock, D.time()); } +void setThreadPriority(ThreadPriority Priority) { +#ifdef HAVE_PTHREAD_H + sched_param priority; + priority.sched_priority = 0; + pthread_setschedparam( + pthread_self(), + Priority == ThreadPriority::Low ? SCHED_IDLE : SCHED_OTHER, &priority); +#endif +} + } // namespace clangd } // namespace clang Index: clangd/index/Background.h =================================================================== --- clangd/index/Background.h +++ clangd/index/Background.h @@ -16,6 +16,7 @@ #include "index/Index.h" #include "clang/Tooling/CompilationDatabase.h" #include "llvm/Support/SHA1.h" +#include "llvm/Support/ThreadPool.h" #include #include #include @@ -34,7 +35,8 @@ // FIXME: resource-dir injection should be hoisted somewhere common. BackgroundIndex(Context BackgroundContext, StringRef ResourceDir, const FileSystemProvider &, - ArrayRef URISchemes = {}); + ArrayRef URISchemes = {}, + size_t ThreadPoolSize = llvm::hardware_concurrency()); ~BackgroundIndex(); // Blocks while the current task finishes. // Enqueue a translation unit for indexing. @@ -66,15 +68,15 @@ llvm::StringMap FileHash; // Digest of indexed file. // queue management - using Task = std::function; // FIXME: use multiple worker threads. - void run(); // Main loop executed by Thread. Runs tasks from Queue. + using Task = std::function; + // Wraps a task with necessary tracking information. + Task wrapTask(Task T); void enqueueLocked(tooling::CompileCommand Cmd); - std::mutex QueueMu; - unsigned NumActiveTasks = 0; // Only idle when queue is empty *and* no tasks. - std::condition_variable QueueCV; + std::mutex QueuedTaskMu; + unsigned NumQueuedTasks = 0; // Only idle when queue is empty *and* no tasks. + std::condition_variable QueuedTaskCV; bool ShouldStop = false; - std::deque Queue; - std::thread Thread; // Must be last, spawned thread reads instance vars. + llvm::ThreadPool Pool; }; } // namespace clangd Index: clangd/index/Background.cpp =================================================================== --- clangd/index/Background.cpp +++ clangd/index/Background.cpp @@ -11,6 +11,7 @@ #include "ClangdUnit.h" #include "Compiler.h" #include "Logger.h" +#include "Threading.h" #include "Trace.h" #include "index/IndexAction.h" #include "index/MemIndex.h" @@ -25,62 +26,73 @@ BackgroundIndex::BackgroundIndex(Context BackgroundContext, StringRef ResourceDir, const FileSystemProvider &FSProvider, - ArrayRef URISchemes) + ArrayRef URISchemes, + size_t ThreadPoolSize) : SwapIndex(make_unique()), ResourceDir(ResourceDir), FSProvider(FSProvider), BackgroundContext(std::move(BackgroundContext)), - URISchemes(URISchemes), Thread([this] { run(); }) {} + URISchemes(URISchemes), Pool(ThreadPoolSize) { + assert(ThreadPoolSize > 0 && "Thread pool size can't be zero."); +} BackgroundIndex::~BackgroundIndex() { stop(); - Thread.join(); + // ThreadPool already waits for all running threads to finish on destruction. + // So, we do not need to wait explicitly for that. } void BackgroundIndex::stop() { { - std::lock_guard Lock(QueueMu); + std::lock_guard Lock(QueuedTaskMu); ShouldStop = true; } - QueueCV.notify_all(); + QueuedTaskCV.notify_all(); } -void BackgroundIndex::run() { - WithContext Background(std::move(BackgroundContext)); - while (true) { - Optional Task; +BackgroundIndex::Task BackgroundIndex::wrapTask(Task T) { + // Set priority to low, since background indexing is a long running task do + // not eat up cpu when there are any other high priority threads. + setThreadPriority(ThreadPriority::Low); + + auto Wrapped = [this](Task T) { + WithContext Background(BackgroundContext.clone()); { - std::unique_lock Lock(QueueMu); - QueueCV.wait(Lock, [&] { return ShouldStop || !Queue.empty(); }); - if (ShouldStop) { - Queue.clear(); - QueueCV.notify_all(); + std::unique_lock Lock(QueuedTaskMu); + if (ShouldStop) return; - } - ++NumActiveTasks; - Task = std::move(Queue.front()); - Queue.pop_front(); } - (*Task)(); + T(); { - std::unique_lock Lock(QueueMu); - assert(NumActiveTasks > 0 && "before decrementing"); - --NumActiveTasks; + std::unique_lock Lock(QueuedTaskMu); + assert(NumQueuedTasks > 0 && "before decrementing"); + --NumQueuedTasks; } - QueueCV.notify_all(); - } + QueuedTaskCV.notify_all(); + }; + return Bind(Wrapped, std::move(T)); } void BackgroundIndex::blockUntilIdleForTest() { - std::unique_lock Lock(QueueMu); - QueueCV.wait(Lock, [&] { return Queue.empty() && NumActiveTasks == 0; }); + std::unique_lock Lock(QueuedTaskMu); + QueuedTaskCV.wait(Lock, [&] { return NumQueuedTasks == 0; }); } void BackgroundIndex::enqueue(StringRef Directory, tooling::CompileCommand Cmd) { + Task T = Bind( + [this](tooling::CompileCommand Cmd) { + std::string Filename = Cmd.Filename; + Cmd.CommandLine.push_back("-resource-dir=" + ResourceDir); + if (auto Error = index(std::move(Cmd))) + log("Indexing {0} failed: {1}", Filename, std::move(Error)); + }, + std::move(Cmd)); + T = wrapTask(std::move(T)); { - std::lock_guard Lock(QueueMu); - enqueueLocked(std::move(Cmd)); + std::unique_lock Lock(QueuedTaskMu); + ++NumQueuedTasks; + QueuedTaskCV.notify_all(); } - QueueCV.notify_all(); + Pool.async(T); } void BackgroundIndex::enqueueAll(StringRef Directory, @@ -93,23 +105,8 @@ std::mt19937 Generator(std::random_device{}()); std::shuffle(Cmds.begin(), Cmds.end(), Generator); log("Enqueueing {0} commands for indexing from {1}", Cmds.size(), Directory); - { - std::lock_guard Lock(QueueMu); - for (auto &Cmd : Cmds) - enqueueLocked(std::move(Cmd)); - } - QueueCV.notify_all(); -} - -void BackgroundIndex::enqueueLocked(tooling::CompileCommand Cmd) { - Queue.push_back(Bind( - [this](tooling::CompileCommand Cmd) { - std::string Filename = Cmd.Filename; - Cmd.CommandLine.push_back("-resource-dir=" + ResourceDir); - if (auto Error = index(std::move(Cmd))) - log("Indexing {0} failed: {1}", Filename, std::move(Error)); - }, - std::move(Cmd))); + for (auto &Cmd : Cmds) + enqueue(Directory, std::move(Cmd)); } Error BackgroundIndex::index(tooling::CompileCommand Cmd) {