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

Reply via email to