[clangd] Added move-only function helpers.
Summary: They are now used in ClangdScheduler instead of deferred std::async computations. The results of `std::async` are much less effective and do not provide a good abstraction for similar purposes, i.e. for storing additional callbacks to clangd async tasks. The actual callback API will follow a bit later. Reviewers: klimek, bkramer, sammccall, krasimir Reviewed By: sammccall Subscribers: cfe-commits Differential Revision: https://reviews.llvm.org/D38627 llvm-svn: 315210
This commit is contained in:
parent
21c75912f5
commit
08e6ccbcf3
|
@ -99,7 +99,7 @@ ClangdScheduler::ClangdScheduler(unsigned AsyncThreadsCount)
|
|||
for (unsigned I = 0; I < AsyncThreadsCount; ++I) {
|
||||
Workers.push_back(std::thread([this]() {
|
||||
while (true) {
|
||||
std::future<void> Request;
|
||||
UniqueFunction<void()> Request;
|
||||
|
||||
// Pick request from the queue
|
||||
{
|
||||
|
@ -120,7 +120,7 @@ ClangdScheduler::ClangdScheduler(unsigned AsyncThreadsCount)
|
|||
RequestQueue.pop_front();
|
||||
} // unlock Mutex
|
||||
|
||||
Request.get();
|
||||
Request();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "llvm/ADT/StringRef.h"
|
||||
|
||||
#include "ClangdUnit.h"
|
||||
#include "Function.h"
|
||||
#include "Protocol.h"
|
||||
|
||||
#include <condition_variable>
|
||||
|
@ -132,9 +133,8 @@ public:
|
|||
|
||||
{
|
||||
std::lock_guard<std::mutex> Lock(Mutex);
|
||||
RequestQueue.push_front(std::async(std::launch::deferred,
|
||||
std::forward<Func>(F),
|
||||
std::forward<Args>(As)...));
|
||||
RequestQueue.push_front(
|
||||
BindWithForward(std::forward<Func>(F), std::forward<Args>(As)...));
|
||||
}
|
||||
RequestCV.notify_one();
|
||||
}
|
||||
|
@ -149,9 +149,8 @@ public:
|
|||
|
||||
{
|
||||
std::lock_guard<std::mutex> Lock(Mutex);
|
||||
RequestQueue.push_back(std::async(std::launch::deferred,
|
||||
std::forward<Func>(F),
|
||||
std::forward<Args>(As)...));
|
||||
RequestQueue.push_back(
|
||||
BindWithForward(std::forward<Func>(F), std::forward<Args>(As)...));
|
||||
}
|
||||
RequestCV.notify_one();
|
||||
}
|
||||
|
@ -167,7 +166,7 @@ private:
|
|||
bool Done = false;
|
||||
/// A queue of requests. Elements of this vector are async computations (i.e.
|
||||
/// results of calling std::async(std::launch::deferred, ...)).
|
||||
std::deque<std::future<void>> RequestQueue;
|
||||
std::deque<UniqueFunction<void()>> RequestQueue;
|
||||
/// Condition variable to wake up worker threads.
|
||||
std::condition_variable RequestCV;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
//===--- Function.h - Utility callable wrappers -----------------*- C++-*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file provides an analogue to std::function that supports move semantics.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_FUNCTION_H
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_FUNCTION_H
|
||||
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace clang {
|
||||
namespace clangd {
|
||||
|
||||
/// A move-only type-erasing function wrapper. Similar to `std::function`, but
|
||||
/// allows to store move-only callables.
|
||||
template <class> class UniqueFunction;
|
||||
|
||||
template <class Ret, class... Args> class UniqueFunction<Ret(Args...)> {
|
||||
public:
|
||||
UniqueFunction() = default;
|
||||
UniqueFunction(std::nullptr_t) : UniqueFunction(){};
|
||||
|
||||
UniqueFunction(UniqueFunction const &) = delete;
|
||||
UniqueFunction &operator=(UniqueFunction const &) = delete;
|
||||
|
||||
UniqueFunction(UniqueFunction &&) noexcept = default;
|
||||
UniqueFunction &operator=(UniqueFunction &&) noexcept = default;
|
||||
|
||||
template <class Callable>
|
||||
UniqueFunction(Callable &&Func)
|
||||
: CallablePtr(llvm::make_unique<
|
||||
FunctionCallImpl<typename std::decay<Callable>::type>>(
|
||||
std::forward<Callable>(Func))) {}
|
||||
|
||||
operator bool() { return CallablePtr; }
|
||||
|
||||
Ret operator()(Args... As) {
|
||||
assert(CallablePtr);
|
||||
CallablePtr->Call(std::forward<Args>(As)...);
|
||||
}
|
||||
|
||||
private:
|
||||
class FunctionCallBase {
|
||||
public:
|
||||
virtual ~FunctionCallBase() = default;
|
||||
virtual Ret Call(Args... As) = 0;
|
||||
};
|
||||
|
||||
template <class Callable>
|
||||
class FunctionCallImpl final : public FunctionCallBase {
|
||||
static_assert(
|
||||
std::is_same<Callable, typename std::decay<Callable>::type>::value,
|
||||
"FunctionCallImpl must be instanstiated with std::decay'ed types");
|
||||
|
||||
public:
|
||||
FunctionCallImpl(Callable Func) : Func(std::move(Func)) {}
|
||||
|
||||
Ret Call(Args... As) override { return Func(std::forward<Args>(As)...); }
|
||||
|
||||
private:
|
||||
Callable Func;
|
||||
};
|
||||
|
||||
std::unique_ptr<FunctionCallBase> CallablePtr;
|
||||
};
|
||||
|
||||
/// Stores a callable object (Func) and arguments (Args) and allows to call the
|
||||
/// callable with provided arguments later using `operator ()`. The arguments
|
||||
/// are std::forward'ed into the callable in the body of `operator()`. Therefore
|
||||
/// `operator()` can only be called once, as some of the arguments could be
|
||||
/// std::move'ed into the callable on first call.
|
||||
template <class Func, class... Args> struct ForwardBinder {
|
||||
using Tuple = std::tuple<typename std::decay<Func>::type,
|
||||
typename std::decay<Args>::type...>;
|
||||
Tuple FuncWithArguments;
|
||||
#ifndef NDEBUG
|
||||
bool WasCalled = false;
|
||||
#endif
|
||||
|
||||
public:
|
||||
ForwardBinder(Tuple FuncWithArguments)
|
||||
: FuncWithArguments(std::move(FuncWithArguments)) {}
|
||||
|
||||
private:
|
||||
template <std::size_t... Indexes, class... RestArgs>
|
||||
auto CallImpl(llvm::integer_sequence<std::size_t, Indexes...> Seq,
|
||||
RestArgs &&... Rest)
|
||||
-> decltype(std::get<0>(this->FuncWithArguments)(
|
||||
std::forward<Args>(std::get<Indexes + 1>(this->FuncWithArguments))...,
|
||||
std::forward<RestArgs>(Rest)...)) {
|
||||
return std::get<0>(this->FuncWithArguments)(
|
||||
std::forward<Args>(std::get<Indexes + 1>(this->FuncWithArguments))...,
|
||||
std::forward<RestArgs>(Rest)...);
|
||||
}
|
||||
|
||||
public:
|
||||
template <class... RestArgs>
|
||||
auto operator()(RestArgs &&... Rest)
|
||||
-> decltype(CallImpl(llvm::index_sequence_for<Args...>(),
|
||||
std::forward<RestArgs>(Rest)...)) {
|
||||
|
||||
#ifndef NDEBUG
|
||||
assert(!WasCalled && "Can only call result of BindWithForward once.");
|
||||
WasCalled = true;
|
||||
#endif
|
||||
return CallImpl(llvm::index_sequence_for<Args...>(),
|
||||
std::forward<RestArgs>(Rest)...);
|
||||
}
|
||||
};
|
||||
|
||||
/// Creates an object that stores a callable (\p F) and first arguments to the
|
||||
/// callable (\p As) and allows to call \p F with \Args at a later point.
|
||||
/// Similar to std::bind, but also works with move-only \p F and \p As.
|
||||
///
|
||||
/// The returned object must be called no more than once, as \p As are
|
||||
/// std::forwarded'ed (therefore can be moved) into \p F during the call.
|
||||
template <class Func, class... Args>
|
||||
ForwardBinder<Func, Args...> BindWithForward(Func F, Args &&... As) {
|
||||
return ForwardBinder<Func, Args...>(
|
||||
std::make_tuple(std::forward<Func>(F), std::forward<Args>(As)...));
|
||||
}
|
||||
|
||||
} // namespace clangd
|
||||
} // namespace clang
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue