1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-21 03:53:04 +02:00
llvm-mirror/include/llvm/ExecutionEngine/Orc/RPCUtils.h
Lang Hames 59acb73541 [ORC][RPC] Fix some bugs in the callB primitive.
Still no unit test due to synchronization bugs on s390. These issues were
discovered in an out-of-tree utility.

llvm-svn: 280163
2016-08-30 21:29:48 +00:00

797 lines
28 KiB
C++

//===----- RPCUTils.h - Basic tilities for building RPC APIs ----*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Basic utilities for building RPC APIs.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_EXECUTIONENGINE_ORC_RPCUTILS_H
#define LLVM_EXECUTIONENGINE_ORC_RPCUTILS_H
#include <map>
#include <vector>
#include "llvm/ADT/STLExtras.h"
#include "llvm/ExecutionEngine/Orc/OrcError.h"
#ifdef _MSC_VER
// concrt.h depends on eh.h for __uncaught_exception declaration
// even if we disable exceptions.
#include <eh.h>
// Disable warnings from ppltasks.h transitively included by <future>.
#pragma warning(push)
#pragma warning(disable : 4530)
#pragma warning(disable : 4062)
#endif
#include <future>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
namespace llvm {
namespace orc {
namespace remote {
/// Describes reserved RPC Function Ids.
///
/// The default implementation will serve for integer and enum function id
/// types. If you want to use a custom type as your FunctionId you can
/// specialize this class and provide unique values for InvalidId,
/// ResponseId and FirstValidId.
template <typename T> class RPCFunctionIdTraits {
public:
static const T InvalidId = static_cast<T>(0);
static const T ResponseId = static_cast<T>(1);
static const T FirstValidId = static_cast<T>(2);
};
// Base class containing utilities that require partial specialization.
// These cannot be included in RPC, as template class members cannot be
// 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
// RPC primitive operations (call, expect, etc) for a given function. It
// is specialized for void and non-void functions to deal with the differences
// betwen the two. Both specializations have the same interface:
//
// Id - The function's unique identifier.
// ErrorReturn - The return type for blocking calls.
// readResult - Deserialize a result from a channel.
// abandon - Abandon a promised result.
// respond - Retun a result on the channel.
template <typename FunctionIdT, FunctionIdT FuncId, typename FnT>
class FunctionHelper {};
// RPC Function description specialization for non-void functions.
template <typename FunctionIdT, FunctionIdT FuncId, typename RetT,
typename... ArgTs>
class FunctionHelper<FunctionIdT, FuncId, RetT(ArgTs...)> {
public:
static_assert(FuncId != RPCFunctionIdTraits<FunctionIdT>::InvalidId &&
FuncId != RPCFunctionIdTraits<FunctionIdT>::ResponseId,
"Cannot define custom function with InvalidId or ResponseId. "
"Please use RPCFunctionTraits<FunctionIdT>::FirstValidId.");
static const FunctionIdT Id = FuncId;
typedef Expected<RetT> ErrorReturn;
// 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<PErrorReturn> &P) {
RetT Val;
auto Err = deserialize(C, Val);
auto Err2 = endReceiveMessage(C);
Err = joinErrors(std::move(Err), std::move(Err2));
if (Err)
return Err;
P.set_value(std::move(Val));
return Error::success();
}
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>
static Error respond(ChannelT &C, SequenceNumberT SeqNo,
ErrorReturn &Result) {
FunctionIdT ResponseId = RPCFunctionIdTraits<FunctionIdT>::ResponseId;
// If the handler returned an error then bail out with that.
if (!Result)
return Result.takeError();
// Otherwise open a new message on the channel and send the result.
if (auto Err = startSendMessage(C))
return Err;
if (auto Err = serializeSeq(C, ResponseId, SeqNo, *Result))
return Err;
return endSendMessage(C);
}
};
// RPC Function description specialization for void functions.
template <typename FunctionIdT, FunctionIdT FuncId, typename... ArgTs>
class FunctionHelper<FunctionIdT, FuncId, void(ArgTs...)> {
public:
static_assert(FuncId != RPCFunctionIdTraits<FunctionIdT>::InvalidId &&
FuncId != RPCFunctionIdTraits<FunctionIdT>::ResponseId,
"Cannot define custom function with InvalidId or ResponseId. "
"Please use RPCFunctionTraits<FunctionIdT>::FirstValidId.");
static const FunctionIdT Id = FuncId;
typedef Error ErrorReturn;
// 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<PErrorReturn> &P) {
// Void functions don't have anything to deserialize, so we're good.
P.set_value(Error::success());
return endReceiveMessage(C);
}
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,
ErrorReturn &Result) {
const FunctionIdT ResponseId =
RPCFunctionIdTraits<FunctionIdT>::ResponseId;
// If the handler returned an error then bail out with that.
if (Result)
return std::move(Result);
// Otherwise open a new message on the channel and send the result.
if (auto Err = startSendMessage(C))
return Err;
if (auto Err = serializeSeq(C, ResponseId, SeqNo))
return Err;
return endSendMessage(C);
}
};
// Helper for the call primitive.
template <typename ChannelT, typename SequenceNumberT, typename Func>
class CallHelper;
template <typename ChannelT, typename SequenceNumberT, typename FunctionIdT,
FunctionIdT FuncId, typename RetT, typename... ArgTs>
class CallHelper<ChannelT, SequenceNumberT,
FunctionHelper<FunctionIdT, FuncId, RetT(ArgTs...)>> {
public:
static Error call(ChannelT &C, SequenceNumberT SeqNo,
const ArgTs &... Args) {
if (auto Err = startSendMessage(C))
return Err;
if (auto Err = serializeSeq(C, FuncId, SeqNo, Args...))
return Err;
return endSendMessage(C);
}
};
// Helper for handle primitive.
template <typename ChannelT, typename SequenceNumberT, typename Func>
class HandlerHelper;
template <typename ChannelT, typename SequenceNumberT, typename FunctionIdT,
FunctionIdT FuncId, typename RetT, typename... ArgTs>
class HandlerHelper<ChannelT, SequenceNumberT,
FunctionHelper<FunctionIdT, FuncId, RetT(ArgTs...)>> {
public:
template <typename HandlerT>
static Error handle(ChannelT &C, HandlerT Handler) {
return readAndHandle(C, Handler, llvm::index_sequence_for<ArgTs...>());
}
private:
typedef FunctionHelper<FunctionIdT, FuncId, RetT(ArgTs...)> Func;
template <typename HandlerT, size_t... Is>
static Error readAndHandle(ChannelT &C, HandlerT Handler,
llvm::index_sequence<Is...> _) {
std::tuple<ArgTs...> RPCArgs;
SequenceNumberT SeqNo;
// GCC 4.7 and 4.8 incorrectly issue a -Wunused-but-set-variable warning
// for RPCArgs. Void cast RPCArgs to work around this for now.
// FIXME: Remove this workaround once we can assume a working GCC version.
(void)RPCArgs;
if (auto Err = deserializeSeq(C, SeqNo, std::get<Is>(RPCArgs)...))
return Err;
// We've deserialized the arguments, so unlock the channel for reading
// before we call the handler. This allows recursive RPC calls.
if (auto Err = endReceiveMessage(C))
return Err;
// Run the handler and get the result.
auto Result = Handler(std::get<Is>(RPCArgs)...);
// Return the result to the client.
return Func::template respond<ChannelT, SequenceNumberT>(C, SeqNo,
Result);
}
};
// Helper for wrapping member functions up as functors.
template <typename ClassT, typename RetT, typename... ArgTs>
class MemberFnWrapper {
public:
typedef RetT (ClassT::*MethodT)(ArgTs...);
MemberFnWrapper(ClassT &Instance, MethodT Method)
: Instance(Instance), Method(Method) {}
RetT operator()(ArgTs &... Args) { return (Instance.*Method)(Args...); }
private:
ClassT &Instance;
MethodT Method;
};
// Helper that provides a Functor for deserializing arguments.
template <typename... ArgTs> class ReadArgs {
public:
Error operator()() { return Error::success(); }
};
template <typename ArgT, typename... ArgTs>
class ReadArgs<ArgT, ArgTs...> : public ReadArgs<ArgTs...> {
public:
ReadArgs(ArgT &Arg, ArgTs &... Args)
: ReadArgs<ArgTs...>(Args...), Arg(Arg) {}
Error operator()(ArgT &ArgVal, ArgTs &... ArgVals) {
this->Arg = std::move(ArgVal);
return ReadArgs<ArgTs...>::operator()(ArgVals...);
}
private:
ArgT &Arg;
};
};
/// Contains primitive utilities for defining, calling and handling calls to
/// remote procedures. ChannelT is a bidirectional stream conforming to the
/// RPCChannel interface (see RPCChannel.h), and FunctionIdT is a procedure
/// identifier type that must be serializable on ChannelT.
///
/// These utilities support the construction of very primitive RPC utilities.
/// Their intent is to ensure correct serialization and deserialization of
/// procedure arguments, and to keep the client and server's view of the API in
/// sync.
///
/// These utilities do not support return values. These can be handled by
/// declaring a corresponding '.*Response' procedure and expecting it after a
/// call). They also do not support versioning: the client and server *must* be
/// compiled with the same procedure definitions.
///
///
///
/// Overview (see comments individual types/methods for details):
///
/// Function<Id, Args...> :
///
/// associates a unique serializable id with an argument list.
///
///
/// call<Func>(Channel, Args...) :
///
/// Calls the remote procedure 'Func' by serializing Func's id followed by its
/// arguments and sending the resulting bytes to 'Channel'.
///
///
/// handle<Func>(Channel, <functor matching Error(Args...)> :
///
/// Handles a call to 'Func' by deserializing its arguments and calling the
/// given functor. This assumes that the id for 'Func' has already been
/// deserialized.
///
/// expect<Func>(Channel, <functor matching Error(Args...)> :
///
/// The same as 'handle', except that the procedure id should not have been
/// read yet. Expect will deserialize the id and assert that it matches Func's
/// id. If it does not, and unexpected RPC call error is returned.
template <typename ChannelT, typename FunctionIdT = uint32_t,
typename SequenceNumberT = uint16_t>
class RPC : public RPCBase {
public:
/// RPC default constructor.
RPC() = default;
/// RPC instances cannot be copied.
RPC(const RPC &) = delete;
/// RPC instances cannot be copied.
RPC &operator=(const RPC &) = delete;
/// RPC move constructor.
// FIXME: Remove once MSVC can synthesize move ops.
RPC(RPC &&Other)
: SequenceNumberMgr(std::move(Other.SequenceNumberMgr)),
OutstandingResults(std::move(Other.OutstandingResults)) {}
/// RPC move assignment.
// FIXME: Remove once MSVC can synthesize move ops.
RPC &operator=(RPC &&Other) {
SequenceNumberMgr = std::move(Other.SequenceNumberMgr);
OutstandingResults = std::move(Other.OutstandingResults);
return *this;
}
/// Utility class for defining/referring to RPC procedures.
///
/// Typedefs of this utility are used when calling/handling remote procedures.
///
/// FuncId should be a unique value of FunctionIdT (i.e. not used with any
/// other Function typedef in the RPC API being defined.
///
/// the template argument Ts... gives the argument list for the remote
/// procedure.
///
/// E.g.
///
/// typedef Function<0, bool> Func1;
/// typedef Function<1, std::string, std::vector<int>> Func2;
///
/// if (auto Err = call<Func1>(Channel, true))
/// /* handle Err */;
///
/// if (auto Err = expect<Func2>(Channel,
/// [](std::string &S, std::vector<int> &V) {
/// // Stuff.
/// return Error::success();
/// })
/// /* handle Err */;
///
template <FunctionIdT FuncId, typename FnT>
using Function = FunctionHelper<FunctionIdT, FuncId, FnT>;
/// Return type for non-blocking call primitives.
template <typename Func>
using NonBlockingCallResult = std::future<typename Func::PErrorReturn>;
/// Return type for non-blocking call-with-seq primitives.
template <typename Func>
using NonBlockingCallWithSeqResult =
std::pair<NonBlockingCallResult<Func>, SequenceNumberT>;
/// 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 appendCallNB method, which does not
/// return the sequence numeber, should be preferred.
template <typename Func, typename... ArgTs>
Expected<NonBlockingCallWithSeqResult<Func>>
appendCallNBWithSeq(ChannelT &C, const ArgTs &... Args) {
auto SeqNo = SequenceNumberMgr.getSequenceNumber();
std::promise<typename Func::PErrorReturn> Promise;
auto Result = Promise.get_future();
OutstandingResults[SeqNo] =
createOutstandingResult<Func>(std::move(Promise));
if (auto Err = CallHelper<ChannelT, SequenceNumberT, Func>::call(C, SeqNo,
Args...)) {
abandonOutstandingResults();
Func::consumeAbandoned(Result);
return std::move(Err);
} else
return NonBlockingCallWithSeqResult<Func>(std::move(Result), SeqNo);
}
/// The same as appendCallNBWithSeq, except that it calls C.send() to
/// flush the channel after serializing the call.
template <typename Func, typename... ArgTs>
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;
}
/// Serialize Args... to channel C, but do not call send.
/// Returns an error if serialization fails, otherwise returns a
/// std::future<Expected<T>> (or a future<Error> for void functions).
template <typename Func, typename... ArgTs>
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 appendCallNB, except that it calls C.send to flush the
/// channel after serializing the call.
template <typename Func, typename... ArgTs>
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<Func>(C, Args...)) {
if (auto Err = C.send()) {
abandonOutstandingResults();
Func::consumeAbandoned(FutureResOrErr->first);
return std::move(Err);
}
return FutureResOrErr->first.get();
} else
return FutureResOrErr.takeError();
}
/// 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 = callNBWithSeq<Func>(C, Args...)) {
auto &ResultAndSeqNo = *ResultAndSeqNoOrErr;
if (auto Err = waitForResult(C, ResultAndSeqNo.second, HandleOther))
return std::move(Err);
return ResultAndSeqNo.first.get();
} else
return ResultAndSeqNoOrErr.takeError();
}
/// 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...);
}
/// Start receiving a new function call.
///
/// Calls startReceiveMessage on the channel, then deserializes a FunctionId
/// into Id.
Error startReceivingFunction(ChannelT &C, FunctionIdT &Id) {
if (auto Err = startReceiveMessage(C))
return Err;
return deserialize(C, Id);
}
/// Deserialize args for Func from C and call Handler. The signature of
/// handler must conform to 'Error(Args...)' where Args... matches
/// the arguments used in the Func typedef.
template <typename Func, typename HandlerT>
static Error handle(ChannelT &C, HandlerT Handler) {
return HandlerHelper<ChannelT, SequenceNumberT, Func>::handle(C, Handler);
}
/// Helper version of 'handle' for calling member functions.
template <typename Func, typename ClassT, typename RetT, typename... ArgTs>
static Error handle(ChannelT &C, ClassT &Instance,
RetT (ClassT::*HandlerMethod)(ArgTs...)) {
return handle<Func>(
C, MemberFnWrapper<ClassT, RetT, ArgTs...>(Instance, HandlerMethod));
}
/// Deserialize a FunctionIdT from C and verify it matches the id for Func.
/// If the id does match, deserialize the arguments and call the handler
/// (similarly to handle).
/// If the id does not match, return an unexpect RPC call error and do not
/// deserialize any further bytes.
template <typename Func, typename HandlerT>
Error expect(ChannelT &C, HandlerT Handler) {
FunctionIdT FuncId;
if (auto Err = startReceivingFunction(C, FuncId))
return std::move(Err);
if (FuncId != Func::Id)
return orcError(OrcErrorCode::UnexpectedRPCCall);
return handle<Func>(C, Handler);
}
/// Helper version of expect for calling member functions.
template <typename Func, typename ClassT, typename... ArgTs>
static Error expect(ChannelT &C, ClassT &Instance,
Error (ClassT::*HandlerMethod)(ArgTs...)) {
return expect<Func>(
C, MemberFnWrapper<ClassT, ArgTs...>(Instance, HandlerMethod));
}
/// Helper for handling setter procedures - this method returns a functor that
/// sets the variables referred to by Args... to values deserialized from the
/// channel.
/// E.g.
///
/// typedef Function<0, bool, int> Func1;
///
/// ...
/// bool B;
/// int I;
/// if (auto Err = expect<Func1>(Channel, readArgs(B, I)))
/// /* Handle Args */ ;
///
template <typename... ArgTs>
static ReadArgs<ArgTs...> readArgs(ArgTs &... Args) {
return ReadArgs<ArgTs...>(Args...);
}
/// Read a response from Channel.
/// This should be called from the receive loop to retrieve results.
Error handleResponse(ChannelT &C, SequenceNumberT *SeqNoRet = nullptr) {
SequenceNumberT SeqNo;
if (auto Err = deserialize(C, SeqNo)) {
abandonOutstandingResults();
return Err;
}
if (SeqNoRet)
*SeqNoRet = SeqNo;
auto I = OutstandingResults.find(SeqNo);
if (I == OutstandingResults.end()) {
abandonOutstandingResults();
return orcError(OrcErrorCode::UnexpectedRPCResponse);
}
if (auto Err = I->second->readResult(C)) {
abandonOutstandingResults();
// FIXME: Release sequence numbers?
return Err;
}
OutstandingResults.erase(I);
SequenceNumberMgr.releaseSequenceNumber(SeqNo);
return Error::success();
}
// Loop waiting for a result with the given sequence number.
// This can be used as a receive loop if the user doesn't have a default.
template <typename HandleOtherFtor>
Error waitForResult(ChannelT &C, SequenceNumberT TgtSeqNo,
HandleOtherFtor &HandleOther = handleNone) {
bool GotTgtResult = false;
while (!GotTgtResult) {
FunctionIdT Id = RPCFunctionIdTraits<FunctionIdT>::InvalidId;
if (auto Err = startReceivingFunction(C, Id))
return Err;
if (Id == RPCFunctionIdTraits<FunctionIdT>::ResponseId) {
SequenceNumberT SeqNo;
if (auto Err = handleResponse(C, &SeqNo))
return Err;
GotTgtResult = (SeqNo == TgtSeqNo);
} else if (auto Err = HandleOther(C, Id))
return Err;
}
return Error::success();
}
// Default handler for 'other' (non-response) functions when waiting for a
// result from the channel.
static Error handleNone(ChannelT &, FunctionIdT) {
return orcError(OrcErrorCode::UnexpectedRPCCall);
};
private:
// Manage sequence numbers.
class SequenceNumberManager {
public:
SequenceNumberManager() = default;
SequenceNumberManager(const SequenceNumberManager &) = delete;
SequenceNumberManager &operator=(const SequenceNumberManager &) = delete;
SequenceNumberManager(SequenceNumberManager &&Other)
: NextSequenceNumber(std::move(Other.NextSequenceNumber)),
FreeSequenceNumbers(std::move(Other.FreeSequenceNumbers)) {}
SequenceNumberManager &operator=(SequenceNumberManager &&Other) {
NextSequenceNumber = std::move(Other.NextSequenceNumber);
FreeSequenceNumbers = std::move(Other.FreeSequenceNumbers);
}
void reset() {
std::lock_guard<std::mutex> Lock(SeqNoLock);
NextSequenceNumber = 0;
FreeSequenceNumbers.clear();
}
SequenceNumberT getSequenceNumber() {
std::lock_guard<std::mutex> Lock(SeqNoLock);
if (FreeSequenceNumbers.empty())
return NextSequenceNumber++;
auto SequenceNumber = FreeSequenceNumbers.back();
FreeSequenceNumbers.pop_back();
return SequenceNumber;
}
void releaseSequenceNumber(SequenceNumberT SequenceNumber) {
std::lock_guard<std::mutex> Lock(SeqNoLock);
FreeSequenceNumbers.push_back(SequenceNumber);
}
private:
std::mutex SeqNoLock;
SequenceNumberT NextSequenceNumber = 0;
std::vector<SequenceNumberT> FreeSequenceNumbers;
};
// Base class for results that haven't been returned from the other end of the
// RPC connection yet.
class OutstandingResult {
public:
virtual ~OutstandingResult() {}
virtual Error readResult(ChannelT &C) = 0;
virtual void abandon() = 0;
};
// Outstanding results for a specific function.
template <typename Func>
class OutstandingResultImpl : public OutstandingResult {
private:
public:
OutstandingResultImpl(std::promise<typename Func::PErrorReturn> &&P)
: P(std::move(P)) {}
Error readResult(ChannelT &C) override { return Func::readResult(C, P); }
void abandon() override { Func::abandon(P); }
private:
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::PErrorReturn> &&P) {
return llvm::make_unique<OutstandingResultImpl<Func>>(std::move(P));
}
// Abandon all outstanding results.
void abandonOutstandingResults() {
for (auto &KV : OutstandingResults)
KV.second->abandon();
OutstandingResults.clear();
SequenceNumberMgr.reset();
}
SequenceNumberManager SequenceNumberMgr;
std::map<SequenceNumberT, std::unique_ptr<OutstandingResult>>
OutstandingResults;
};
} // end namespace remote
} // end namespace orc
} // end namespace llvm
#endif