[llvm] r280141 - Re-instate recent RPC updates (r280016, r280017, r280027, r280051) with a

Lang Hames via llvm-commits llvm-commits at lists.llvm.org
Tue Aug 30 12:56:16 PDT 2016


Author: lhames
Date: Tue Aug 30 14:56:15 2016
New Revision: 280141

URL: http://llvm.org/viewvc/llvm-project?rev=280141&view=rev
Log:
Re-instate recent RPC updates (r280016, r280017, r280027, r280051) with a
workaround for the limitations of MSVC 2013's std::future class.

Modified:
    llvm/trunk/include/llvm/ExecutionEngine/Orc/RPCUtils.h
    llvm/trunk/unittests/ExecutionEngine/Orc/RPCUtilsTest.cpp

Modified: llvm/trunk/include/llvm/ExecutionEngine/Orc/RPCUtils.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/ExecutionEngine/Orc/RPCUtils.h?rev=280141&r1=280140&r2=280141&view=diff
==============================================================================
--- llvm/trunk/include/llvm/ExecutionEngine/Orc/RPCUtils.h (original)
+++ llvm/trunk/include/llvm/ExecutionEngine/Orc/RPCUtils.h Tue Aug 30 14:56:15 2016
@@ -17,7 +17,6 @@
 #include <map>
 #include <vector>
 
-#include "llvm/ADT/Optional.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ExecutionEngine/Orc/OrcError.h"
 
@@ -61,6 +60,71 @@ public:
 // partially specialized.
 class RPCBase {
 protected:
+
+  // FIXME: Remove MSVCPError/MSVCPExpected once MSVC's future implementation
+  //        supports classes without default constructors.
+#ifdef _MSC_VER
+
+  // Work around MSVC's future implementation's use of default constructors:
+  // A default constructed value in the promise will be overwritten when the
+  // real error is set - so the default constructed Error has to be checked
+  // already.
+  class MSVCPError : public Error {
+  public:
+
+    MSVCPError() {
+      (void)!!*this;
+    }
+
+    MSVCPError(MSVCPError &&Other) : Error(std::move(Other)) {}
+
+    MSVCPError& operator=(MSVCPError Other) {
+      Error::operator=(std::move(Other));
+      return *this;
+    }
+
+    MSVCPError(Error Err) : Error(std::move(Err)) {}
+  };
+
+  // Likewise for Expected:
+  template <typename T>
+  class MSVCPExpected : public Expected<T> {
+  public:
+
+    MSVCPExpected()
+        : Expected<T>(make_error<StringError>("", inconvertibleErrorCode())) {
+      consumeError(this->takeError());
+    }
+
+    MSVCPExpected(MSVCPExpected &&Other) : Expected<T>(std::move(Other)) {}
+
+    MSVCPExpected& operator=(MSVCPExpected &&Other) {
+      Expected<T>::operator=(std::move(Other));
+      return *this;
+    }
+
+    MSVCPExpected(Error Err) : Expected<T>(std::move(Err)) {}
+
+    template <typename OtherT>
+    MSVCPExpected(OtherT &&Val,
+                  typename std::enable_if<std::is_convertible<OtherT, T>::value>::type
+                  * = nullptr) : Expected<T>(std::move(Val)) {}
+
+    template <class OtherT>
+    MSVCPExpected(
+        Expected<OtherT> &&Other,
+        typename std::enable_if<std::is_convertible<OtherT, T>::value>::type * =
+        nullptr) : Expected<T>(std::move(Other)) {}
+
+    template <class OtherT>
+    explicit MSVCPExpected(
+        Expected<OtherT> &&Other,
+        typename std::enable_if<!std::is_convertible<OtherT, T>::value>::type * =
+        nullptr) : Expected<T>(std::move(Other)) {}
+  };
+
+#endif // _MSC_VER
+
   // RPC Function description type.
   //
   // This class provides the information and operations needed to support the
@@ -69,12 +133,9 @@ protected:
   // betwen the two. Both specializations have the same interface:
   //
   // Id - The function's unique identifier.
-  // OptionalReturn - The return type for asyncronous calls.
-  // ErrorReturn - The return type for synchronous calls.
-  // optionalToErrorReturn - Conversion from a valid OptionalReturn to an
-  //                         ErrorReturn.
+  // ErrorReturn - The return type for blocking calls.
   // readResult - Deserialize a result from a channel.
-  // abandon - Abandon a promised (asynchronous) result.
+  // abandon - Abandon a promised result.
   // respond - Retun a result on the channel.
   template <typename FunctionIdT, FunctionIdT FuncId, typename FnT>
   class FunctionHelper {};
@@ -91,32 +152,38 @@ protected:
 
     static const FunctionIdT Id = FuncId;
 
-    typedef Optional<RetT> OptionalReturn;
-
     typedef Expected<RetT> ErrorReturn;
 
-    static ErrorReturn optionalToErrorReturn(OptionalReturn &&V) {
-      assert(V && "Return value not available");
-      return std::move(*V);
-    }
+    // FIXME: Ditch PErrorReturn (replace it with plain ErrorReturn) once MSVC's
+    //        std::future implementation supports types without default
+    //        constructors.
+#ifdef _MSC_VER
+    typedef MSVCPExpected<RetT> PErrorReturn;
+#else
+    typedef Expected<RetT> PErrorReturn;
+#endif
 
     template <typename ChannelT>
-    static Error readResult(ChannelT &C, std::promise<OptionalReturn> &P) {
+    static Error readResult(ChannelT &C, std::promise<PErrorReturn> &P) {
       RetT Val;
       auto Err = deserialize(C, Val);
       auto Err2 = endReceiveMessage(C);
       Err = joinErrors(std::move(Err), std::move(Err2));
-
-      if (Err) {
-        P.set_value(OptionalReturn());
+      if (Err)
         return Err;
-      }
+
       P.set_value(std::move(Val));
       return Error::success();
     }
 
-    static void abandon(std::promise<OptionalReturn> &P) {
-      P.set_value(OptionalReturn());
+    static void abandon(std::promise<PErrorReturn> &P) {
+      P.set_value(
+        make_error<StringError>("RPC function call failed to return",
+                                inconvertibleErrorCode()));
+    }
+
+    static void consumeAbandoned(std::future<PErrorReturn> &P) {
+      consumeError(P.get().takeError());
     }
 
     template <typename ChannelT, typename SequenceNumberT>
@@ -148,22 +215,33 @@ protected:
 
     static const FunctionIdT Id = FuncId;
 
-    typedef bool OptionalReturn;
     typedef Error ErrorReturn;
 
-    static ErrorReturn optionalToErrorReturn(OptionalReturn &&V) {
-      assert(V && "Return value not available");
-      return Error::success();
-    }
+    // FIXME: Ditch PErrorReturn (replace it with plain ErrorReturn) once MSVC's
+    //        std::future implementation supports types without default
+    //        constructors.
+#ifdef _MSC_VER
+    typedef MSVCPError PErrorReturn;
+#else
+    typedef Error PErrorReturn;
+#endif
 
     template <typename ChannelT>
-    static Error readResult(ChannelT &C, std::promise<OptionalReturn> &P) {
+    static Error readResult(ChannelT &C, std::promise<PErrorReturn> &P) {
       // Void functions don't have anything to deserialize, so we're good.
-      P.set_value(true);
+      P.set_value(Error::success());
       return endReceiveMessage(C);
     }
 
-    static void abandon(std::promise<OptionalReturn> &P) { P.set_value(false); }
+    static void abandon(std::promise<PErrorReturn> &P) {
+      P.set_value(
+        make_error<StringError>("RPC function call failed to return",
+                                inconvertibleErrorCode()));
+    }
+
+    static void consumeAbandoned(std::future<PErrorReturn> &P) {
+      consumeError(P.get());
+    }
 
     template <typename ChannelT, typename SequenceNumberT>
     static Error respond(ChannelT &C, SequenceNumberT SeqNo,
@@ -378,30 +456,27 @@ public:
   template <FunctionIdT FuncId, typename FnT>
   using Function = FunctionHelper<FunctionIdT, FuncId, FnT>;
 
-  /// Return type for asynchronous call primitives.
+  /// Return type for non-blocking call primitives.
   template <typename Func>
-  using AsyncCallResult = std::future<typename Func::OptionalReturn>;
+  using NonBlockingCallResult = std::future<typename Func::PErrorReturn>;
 
-  /// Return type for asynchronous call-with-seq primitives.
+  /// Return type for non-blocking call-with-seq primitives.
   template <typename Func>
-  using AsyncCallWithSeqResult =
-      std::pair<std::future<typename Func::OptionalReturn>, SequenceNumberT>;
+  using NonBlockingCallWithSeqResult =
+      std::pair<NonBlockingCallResult<Func>, SequenceNumberT>;
 
-  /// Serialize Args... to channel C, but do not call C.send().
-  ///
-  /// Returns an error (on serialization failure) or a pair of:
-  /// (1) A future Optional<T> (or future<bool> for void functions), and
-  /// (2) A sequence number.
+  /// Call Func on Channel C. Does not block, does not call send. Returns a pair
+  /// of a future result and the sequence number assigned to the result.
   ///
   /// This utility function is primarily used for single-threaded mode support,
   /// where the sequence number can be used to wait for the corresponding
-  /// result. In multi-threaded mode the appendCallAsync method, which does not
+  /// result. In multi-threaded mode the appendCallNB method, which does not
   /// return the sequence numeber, should be preferred.
   template <typename Func, typename... ArgTs>
-  Expected<AsyncCallWithSeqResult<Func>>
-  appendCallAsyncWithSeq(ChannelT &C, const ArgTs &... Args) {
+  Expected<NonBlockingCallWithSeqResult<Func>>
+  appendCallNBWithSeq(ChannelT &C, const ArgTs &... Args) {
     auto SeqNo = SequenceNumberMgr.getSequenceNumber();
-    std::promise<typename Func::OptionalReturn> Promise;
+    std::promise<typename Func::PErrorReturn> Promise;
     auto Result = Promise.get_future();
     OutstandingResults[SeqNo] =
         createOutstandingResult<Func>(std::move(Promise));
@@ -409,21 +484,23 @@ public:
     if (auto Err = CallHelper<ChannelT, SequenceNumberT, Func>::call(C, SeqNo,
                                                                      Args...)) {
       abandonOutstandingResults();
+      Func::consumeAbandoned(Result);
       return std::move(Err);
     } else
-      return AsyncCallWithSeqResult<Func>(std::move(Result), SeqNo);
+      return NonBlockingCallWithSeqResult<Func>(std::move(Result), SeqNo);
   }
 
-  /// The same as appendCallAsyncWithSeq, except that it calls C.send() to
+  /// The same as appendCallNBWithSeq, except that it calls C.send() to
   /// flush the channel after serializing the call.
   template <typename Func, typename... ArgTs>
-  Expected<AsyncCallWithSeqResult<Func>>
-  callAsyncWithSeq(ChannelT &C, const ArgTs &... Args) {
-    auto Result = appendCallAsyncWithSeq<Func>(C, Args...);
+  Expected<NonBlockingCallWithSeqResult<Func>>
+  callNBWithSeq(ChannelT &C, const ArgTs &... Args) {
+    auto Result = appendCallNBWithSeq<Func>(C, Args...);
     if (!Result)
       return Result;
     if (auto Err = C.send()) {
       abandonOutstandingResults();
+      Func::consumeAbandoned(Result->first);
       return std::move(Err);
     }
     return Result;
@@ -431,41 +508,66 @@ public:
 
   /// Serialize Args... to channel C, but do not call send.
   /// Returns an error if serialization fails, otherwise returns a
-  /// std::future<Optional<T>> (or a future<bool> for void functions).
+  /// std::future<Expected<T>> (or a future<Error> for void functions).
   template <typename Func, typename... ArgTs>
-  Expected<AsyncCallResult<Func>> appendCallAsync(ChannelT &C,
-                                                  const ArgTs &... Args) {
-    auto ResAndSeqOrErr = appendCallAsyncWithSeq<Func>(C, Args...);
-    if (ResAndSeqOrErr)
-      return std::move(ResAndSeqOrErr->first);
-    return ResAndSeqOrErr.getError();
+  Expected<NonBlockingCallResult<Func>> appendCallNB(ChannelT &C,
+                                                     const ArgTs &... Args) {
+    auto FutureResAndSeqOrErr = appendCallNBWithSeq<Func>(C, Args...);
+    if (FutureResAndSeqOrErr)
+      return std::move(FutureResAndSeqOrErr->first);
+    return FutureResAndSeqOrErr.takeError();
   }
 
-  /// The same as appendCallAsync, except that it calls C.send to flush the
+  /// The same as appendCallNB, except that it calls C.send to flush the
   /// channel after serializing the call.
   template <typename Func, typename... ArgTs>
-  Expected<AsyncCallResult<Func>> callAsync(ChannelT &C,
-                                            const ArgTs &... Args) {
-    auto ResAndSeqOrErr = callAsyncWithSeq<Func>(C, Args...);
-    if (ResAndSeqOrErr)
-      return std::move(ResAndSeqOrErr->first);
-    return ResAndSeqOrErr.getError();
+  Expected<NonBlockingCallResult<Func>> callNB(ChannelT &C,
+                                               const ArgTs &... Args) {
+    auto FutureResAndSeqOrErr = callNBWithSeq<Func>(C, Args...);
+    if (FutureResAndSeqOrErr)
+      return std::move(FutureResAndSeqOrErr->first);
+    return FutureResAndSeqOrErr.takeError();
+  }
+
+  /// Call Func on Channel C. Blocks waiting for a result. Returns an Error
+  /// for void functions or an Expected<T> for functions returning a T.
+  ///
+  /// This function is for use in threaded code where another thread is
+  /// handling responses and incoming calls.
+  template <typename Func, typename... ArgTs>
+  typename Func::ErrorReturn callB(ChannelT &C, const ArgTs &... Args) {
+    if (auto FutureResOrErr = callNBWithSeq(C, Args...)) {
+      if (auto Err = C.send()) {
+        abandonOutstandingResults();
+        Func::consumeAbandoned(*FutureResOrErr);
+        return std::move(Err);
+      }
+      return FutureResOrErr->get();
+    } else
+      return FutureResOrErr.takeError();
   }
 
-  /// This can be used in single-threaded mode.
+  /// Call Func on Channel C. Block waiting for a result. While blocked, run
+  /// HandleOther to handle incoming calls (Response calls will be handled
+  /// implicitly before calling HandleOther). Returns an Error for void
+  /// functions or an Expected<T> for functions returning a T.
+  ///
+  /// This function is for use in single threaded mode when the calling thread
+  /// must act as both sender and receiver.
   template <typename Func, typename HandleFtor, typename... ArgTs>
   typename Func::ErrorReturn
   callSTHandling(ChannelT &C, HandleFtor &HandleOther, const ArgTs &... Args) {
-    if (auto ResultAndSeqNoOrErr = callAsyncWithSeq<Func>(C, Args...)) {
+    if (auto ResultAndSeqNoOrErr = callNBWithSeq<Func>(C, Args...)) {
       auto &ResultAndSeqNo = *ResultAndSeqNoOrErr;
       if (auto Err = waitForResult(C, ResultAndSeqNo.second, HandleOther))
         return std::move(Err);
-      return Func::optionalToErrorReturn(ResultAndSeqNo.first.get());
+      return ResultAndSeqNo.first.get();
     } else
       return ResultAndSeqNoOrErr.takeError();
   }
 
-  // This can be used in single-threaded mode.
+  /// Call Func on Channel C. Block waiting for a result. Returns an Error for
+  /// void functions or an Expected<T> for functions returning a T.
   template <typename Func, typename... ArgTs>
   typename Func::ErrorReturn callST(ChannelT &C, const ArgTs &... Args) {
     return callSTHandling<Func>(C, handleNone, Args...);
@@ -656,7 +758,7 @@ private:
   class OutstandingResultImpl : public OutstandingResult {
   private:
   public:
-    OutstandingResultImpl(std::promise<typename Func::OptionalReturn> &&P)
+    OutstandingResultImpl(std::promise<typename Func::PErrorReturn> &&P)
         : P(std::move(P)) {}
 
     Error readResult(ChannelT &C) override { return Func::readResult(C, P); }
@@ -664,13 +766,13 @@ private:
     void abandon() override { Func::abandon(P); }
 
   private:
-    std::promise<typename Func::OptionalReturn> P;
+    std::promise<typename Func::PErrorReturn> P;
   };
 
   // Create an outstanding result for the given function.
   template <typename Func>
   std::unique_ptr<OutstandingResult>
-  createOutstandingResult(std::promise<typename Func::OptionalReturn> &&P) {
+  createOutstandingResult(std::promise<typename Func::PErrorReturn> &&P) {
     return llvm::make_unique<OutstandingResultImpl<Func>>(std::move(P));
   }
 

Modified: llvm/trunk/unittests/ExecutionEngine/Orc/RPCUtilsTest.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/ExecutionEngine/Orc/RPCUtilsTest.cpp?rev=280141&r1=280140&r2=280141&view=diff
==============================================================================
--- llvm/trunk/unittests/ExecutionEngine/Orc/RPCUtilsTest.cpp (original)
+++ llvm/trunk/unittests/ExecutionEngine/Orc/RPCUtilsTest.cpp Tue Aug 30 14:56:15 2016
@@ -83,7 +83,7 @@ TEST_F(DummyRPC, TestAsyncVoidBool) {
   QueueChannel C2(Q2, Q1);
 
   // Make an async call.
-  auto ResOrErr = callAsyncWithSeq<VoidBool>(C1, true);
+  auto ResOrErr = callNBWithSeq<VoidBool>(C1, true);
   EXPECT_TRUE(!!ResOrErr) << "Simple call over queue failed";
 
   {
@@ -102,8 +102,8 @@ TEST_F(DummyRPC, TestAsyncVoidBool) {
   }
 
   // Verify that the function returned ok.
-  auto Val = ResOrErr->first.get();
-  EXPECT_TRUE(Val) << "Remote void function failed to execute.";
+  auto Err = ResOrErr->first.get();
+  EXPECT_FALSE(!!Err) << "Remote void function failed to execute.";
 }
 
 TEST_F(DummyRPC, TestAsyncIntInt) {
@@ -112,7 +112,7 @@ TEST_F(DummyRPC, TestAsyncIntInt) {
   QueueChannel C2(Q2, Q1);
 
   // Make an async call.
-  auto ResOrErr = callAsyncWithSeq<IntInt>(C1, 21);
+  auto ResOrErr = callNBWithSeq<IntInt>(C1, 21);
   EXPECT_TRUE(!!ResOrErr) << "Simple call over queue failed";
 
   {
@@ -143,7 +143,7 @@ TEST_F(DummyRPC, TestSerialization) {
 
   // Make a call to Proc1.
   std::vector<int> v({42, 7});
-  auto ResOrErr = callAsyncWithSeq<AllTheTypes>(
+  auto ResOrErr = callNBWithSeq<AllTheTypes>(
       C1, -101, 250, -10000, 10000, -1000000000, 1000000000, -10000000000,
       10000000000, true, "foo", v);
   EXPECT_TRUE(!!ResOrErr) << "Big (serialization test) call over queue failed";
@@ -179,8 +179,8 @@ TEST_F(DummyRPC, TestSerialization) {
   }
 
   // Verify that the function returned ok.
-  auto Val = ResOrErr->first.get();
-  EXPECT_TRUE(Val) << "Remote void function failed to execute.";
+  auto Err = ResOrErr->first.get();
+  EXPECT_FALSE(!!Err) << "Remote void function failed to execute.";
 }
 
 // Test the synchronous call API.




More information about the llvm-commits mailing list