ilya-biryukov created this revision. 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.
https://reviews.llvm.org/D38627 Files: clangd/ClangdServer.cpp clangd/ClangdServer.h clangd/Function.h
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> class UniqueFunction; + +template <class Ret, class... Args> class UniqueFunction<Ret(Args...)> { +public: + UniqueFunction() = default; + + 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))...)) { + 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 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 <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 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 <condition_variable> @@ -132,9 +133,8 @@ { 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(UniqueFunction<void()>( + BindWithForward(std::forward<Func>(F), std::forward<Args>(As)...))); } RequestCV.notify_one(); } @@ -149,9 +149,8 @@ { 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(UniqueFunction<void()>( + BindWithForward(std::forward<Func>(F), std::forward<Args>(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<std::future<void>> RequestQueue; + std::deque<UniqueFunction<void()>> 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<void> Request; + UniqueFunction<void()> Request; // Pick request from the queue { @@ -120,7 +120,7 @@ RequestQueue.pop_front(); } // unlock Mutex - Request.get(); + Request(); } })); }
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits