summaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
authorLang Hames <lhames@gmail.com>2016-08-30 19:56:15 +0000
committerLang Hames <lhames@gmail.com>2016-08-30 19:56:15 +0000
commit7ebede93b7b2abe1eeb6ddec3ffb8807ae48a4f2 (patch)
tree7c9352ef1193c2fdd29bbb47798fba2bdc3027de /include
parentb16d4d34414cdac221f6ebd115dbe5b8e85a8942 (diff)
Re-instate recent RPC updates (r280016, r280017, r280027, r280051) with a
workaround for the limitations of MSVC 2013's std::future class. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@280141 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'include')
-rw-r--r--include/llvm/ExecutionEngine/Orc/RPCUtils.h236
1 files changed, 169 insertions, 67 deletions
diff --git a/include/llvm/ExecutionEngine/Orc/RPCUtils.h b/include/llvm/ExecutionEngine/Orc/RPCUtils.h
index 966a4968434..2e7b7b8b25c 100644
--- a/include/llvm/ExecutionEngine/Orc/RPCUtils.h
+++ b/include/llvm/ExecutionEngine/Orc/RPCUtils.h
@@ -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));
}