Index: clangd/ClangdServer.h =================================================================== --- clangd/ClangdServer.h +++ clangd/ClangdServer.h @@ -20,6 +20,7 @@ #include "llvm/ADT/StringRef.h" #include "ClangdUnit.h" +#include "Function.h" #include "Protocol.h" #include @@ -132,9 +133,8 @@ { std::lock_guard Lock(Mutex); - RequestQueue.push_front(std::async(std::launch::deferred, - std::forward(F), - std::forward(As)...)); + RequestQueue.push_front(UniqueFunction( + BindWithForward(std::forward(F), std::forward(As)...))); } RequestCV.notify_one(); } @@ -149,9 +149,8 @@ { std::lock_guard Lock(Mutex); - RequestQueue.push_back(std::async(std::launch::deferred, - std::forward(F), - std::forward(As)...)); + RequestQueue.push_back(UniqueFunction( + BindWithForward(std::forward(F), std::forward(As)...))); } RequestCV.notify_one(); } @@ -167,7 +166,7 @@ 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> RequestQueue; + std::deque> RequestQueue; /// Condition variable to wake up worker threads. std::condition_variable RequestCV; }; Index: clangd/ClangdServer.cpp =================================================================== --- clangd/ClangdServer.cpp +++ clangd/ClangdServer.cpp @@ -99,7 +99,7 @@ for (unsigned I = 0; I < AsyncThreadsCount; ++I) { Workers.push_back(std::thread([this]() { while (true) { - std::future Request; + UniqueFunction Request; // Pick request from the queue { @@ -120,7 +120,7 @@ RequestQueue.pop_front(); } // unlock Mutex - Request.get(); + Request(); } })); } Index: clangd/Function.h =================================================================== --- /dev/null +++ clangd/Function.h @@ -0,0 +1,126 @@ +//===--- 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. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_FUNCTION_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_FUNCTION_H + +namespace clang { +namespace clangd { + +/// A move-only type-erasing function wrapper. Similar to `std::function`, but +/// allows to store move-only callables. +template class UniqueFunction; + +template class UniqueFunction { +public: + UniqueFunction() = default; + + UniqueFunction(UniqueFunction const &) = delete; + UniqueFunction &operator=(UniqueFunction const &) = delete; + + UniqueFunction(UniqueFunction &&) noexcept = default; + UniqueFunction &operator=(UniqueFunction &&) noexcept = default; + + template + UniqueFunction(Callable Func) + : CallablePtr(llvm::make_unique< + FunctionCallImpl::type>>( + std::forward(Func))) {} + + operator bool() { return CallablePtr; } + + Ret operator()(Args... As) { + assert(CallablePtr); + CallablePtr->Call(std::forward(As)...); + } + +private: + class FunctionCallBase { + public: + virtual ~FunctionCallBase() = default; + virtual Ret Call(Args... As) = 0; + }; + + template + class FunctionCallImpl final : public FunctionCallBase { + static_assert( + std::is_same::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(As)...); } + + private: + Callable Func; + }; + + std::unique_ptr 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 struct ForwardBinder { + using Tuple = std::tuple::type, + typename std::decay::type...>; + Tuple FuncWithArguments; +#ifndef NDEBUG + bool WasCalled = false; +#endif + +public: + ForwardBinder(Tuple FuncWithArguments) + : FuncWithArguments(std::move(FuncWithArguments)) {} + +private: + template + auto CallImpl(llvm::integer_sequence Seq, + RestArgs... Rest) + -> decltype(std::get<0>(this->FuncWithArguments)(std::forward( + std::get(this->FuncWithArguments))...)) { + return std::get<0>(this->FuncWithArguments)( + std::forward(std::get(this->FuncWithArguments))..., + std::forward(Rest)...); + } + +public: + template + auto operator()(RestArgs... Rest) + -> decltype(CallImpl(llvm::index_sequence_for(), + std::forward(Rest)...)) { + +#ifndef NDEBUG + assert(!WasCalled && "Can only call result of BindWithForward once."); + WasCalled = true; +#endif + return CallImpl(llvm::index_sequence_for(), + std::forward(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 can only be called once, as \p As are std::forwarded'ed +/// (therefore can be std::move`d) into \p F for the call. +template +ForwardBinder BindWithForward(Func F, Args... As) { + return ForwardBinder( + std::make_tuple(std::forward(F), std::forward(As)...)); +} + +} // namespace clangd +} // namespace clang + +#endif