summaryrefslogtreecommitdiffstats
path: root/include/llvm/ExecutionEngine/Orc/RPCUtils.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/llvm/ExecutionEngine/Orc/RPCUtils.h')
-rw-r--r--include/llvm/ExecutionEngine/Orc/RPCUtils.h266
1 files changed, 266 insertions, 0 deletions
diff --git a/include/llvm/ExecutionEngine/Orc/RPCUtils.h b/include/llvm/ExecutionEngine/Orc/RPCUtils.h
new file mode 100644
index 0000000..0bd5cbc
--- /dev/null
+++ b/include/llvm/ExecutionEngine/Orc/RPCUtils.h
@@ -0,0 +1,266 @@
+//===----- 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 "llvm/ADT/STLExtras.h"
+
+namespace llvm {
+namespace orc {
+namespace remote {
+
+// 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:
+ template <typename ProcedureIdT, ProcedureIdT ProcId, typename... Ts>
+ class ProcedureHelper {
+ public:
+ static const ProcedureIdT Id = ProcId;
+ };
+
+ template <typename ChannelT, typename Proc> class CallHelper;
+
+ template <typename ChannelT, typename ProcedureIdT, ProcedureIdT ProcId,
+ typename... ArgTs>
+ class CallHelper<ChannelT, ProcedureHelper<ProcedureIdT, ProcId, ArgTs...>> {
+ public:
+ static std::error_code call(ChannelT &C, const ArgTs &... Args) {
+ if (auto EC = serialize(C, ProcId))
+ return EC;
+ // If you see a compile-error on this line you're probably calling a
+ // function with the wrong signature.
+ return serialize_seq(C, Args...);
+ }
+ };
+
+ template <typename ChannelT, typename Proc> class HandlerHelper;
+
+ template <typename ChannelT, typename ProcedureIdT, ProcedureIdT ProcId,
+ typename... ArgTs>
+ class HandlerHelper<ChannelT,
+ ProcedureHelper<ProcedureIdT, ProcId, ArgTs...>> {
+ public:
+ template <typename HandlerT>
+ static std::error_code handle(ChannelT &C, HandlerT Handler) {
+ return readAndHandle(C, Handler, llvm::index_sequence_for<ArgTs...>());
+ }
+
+ private:
+ template <typename HandlerT, size_t... Is>
+ static std::error_code readAndHandle(ChannelT &C, HandlerT Handler,
+ llvm::index_sequence<Is...> _) {
+ std::tuple<ArgTs...> RPCArgs;
+ if (auto EC = deserialize_seq(C, std::get<Is>(RPCArgs)...))
+ return EC;
+ return Handler(std::get<Is>(RPCArgs)...);
+ }
+ };
+
+ template <typename ClassT, typename... ArgTs> class MemberFnWrapper {
+ public:
+ typedef std::error_code (ClassT::*MethodT)(ArgTs...);
+ MemberFnWrapper(ClassT &Instance, MethodT Method)
+ : Instance(Instance), Method(Method) {}
+ std::error_code operator()(ArgTs &... Args) {
+ return (Instance.*Method)(Args...);
+ }
+
+ private:
+ ClassT &Instance;
+ MethodT Method;
+ };
+
+ template <typename... ArgTs> class ReadArgs {
+ public:
+ std::error_code operator()() { return std::error_code(); }
+ };
+
+ template <typename ArgT, typename... ArgTs>
+ class ReadArgs<ArgT, ArgTs...> : public ReadArgs<ArgTs...> {
+ public:
+ ReadArgs(ArgT &Arg, ArgTs &... Args)
+ : ReadArgs<ArgTs...>(Args...), Arg(Arg) {}
+
+ std::error_code 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 ProcedureIdT 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):
+///
+/// Procedure<Id, Args...> :
+///
+/// associates a unique serializable id with an argument list.
+///
+///
+/// call<Proc>(Channel, Args...) :
+///
+/// Calls the remote procedure 'Proc' by serializing Proc's id followed by its
+/// arguments and sending the resulting bytes to 'Channel'.
+///
+///
+/// handle<Proc>(Channel, <functor matching std::error_code(Args...)> :
+///
+/// Handles a call to 'Proc' by deserializing its arguments and calling the
+/// given functor. This assumes that the id for 'Proc' has already been
+/// deserialized.
+///
+/// expect<Proc>(Channel, <functor matching std::error_code(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 Proc's
+/// id. If it does not, and unexpected RPC call error is returned.
+
+template <typename ChannelT, typename ProcedureIdT = uint32_t>
+class RPC : public RPCBase {
+public:
+ /// Utility class for defining/referring to RPC procedures.
+ ///
+ /// Typedefs of this utility are used when calling/handling remote procedures.
+ ///
+ /// ProcId should be a unique value of ProcedureIdT (i.e. not used with any
+ /// other Procedure typedef in the RPC API being defined.
+ ///
+ /// the template argument Ts... gives the argument list for the remote
+ /// procedure.
+ ///
+ /// E.g.
+ ///
+ /// typedef Procedure<0, bool> Proc1;
+ /// typedef Procedure<1, std::string, std::vector<int>> Proc2;
+ ///
+ /// if (auto EC = call<Proc1>(Channel, true))
+ /// /* handle EC */;
+ ///
+ /// if (auto EC = expect<Proc2>(Channel,
+ /// [](std::string &S, std::vector<int> &V) {
+ /// // Stuff.
+ /// return std::error_code();
+ /// })
+ /// /* handle EC */;
+ ///
+ template <ProcedureIdT ProcId, typename... Ts>
+ using Procedure = ProcedureHelper<ProcedureIdT, ProcId, Ts...>;
+
+ /// Serialize Args... to channel C, but do not call C.send().
+ ///
+ /// For buffered channels, this can be used to queue up several calls before
+ /// flushing the channel.
+ template <typename Proc, typename... ArgTs>
+ static std::error_code appendCall(ChannelT &C, const ArgTs &... Args) {
+ return CallHelper<ChannelT, Proc>::call(C, Args...);
+ }
+
+ /// Serialize Args... to channel C and call C.send().
+ template <typename Proc, typename... ArgTs>
+ static std::error_code call(ChannelT &C, const ArgTs &... Args) {
+ if (auto EC = appendCall<Proc>(C, Args...))
+ return EC;
+ return C.send();
+ }
+
+ /// Deserialize and return an enum whose underlying type is ProcedureIdT.
+ static std::error_code getNextProcId(ChannelT &C, ProcedureIdT &Id) {
+ return deserialize(C, Id);
+ }
+
+ /// Deserialize args for Proc from C and call Handler. The signature of
+ /// handler must conform to 'std::error_code(Args...)' where Args... matches
+ /// the arguments used in the Proc typedef.
+ template <typename Proc, typename HandlerT>
+ static std::error_code handle(ChannelT &C, HandlerT Handler) {
+ return HandlerHelper<ChannelT, Proc>::handle(C, Handler);
+ }
+
+ /// Helper version of 'handle' for calling member functions.
+ template <typename Proc, typename ClassT, typename... ArgTs>
+ static std::error_code
+ handle(ChannelT &C, ClassT &Instance,
+ std::error_code (ClassT::*HandlerMethod)(ArgTs...)) {
+ return handle<Proc>(
+ C, MemberFnWrapper<ClassT, ArgTs...>(Instance, HandlerMethod));
+ }
+
+ /// Deserialize a ProcedureIdT from C and verify it matches the id for Proc.
+ /// 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 Proc, typename HandlerT>
+ static std::error_code expect(ChannelT &C, HandlerT Handler) {
+ ProcedureIdT ProcId;
+ if (auto EC = getNextProcId(C, ProcId))
+ return EC;
+ if (ProcId != Proc::Id)
+ return orcError(OrcErrorCode::UnexpectedRPCCall);
+ return handle<Proc>(C, Handler);
+ }
+
+ /// Helper version of expect for calling member functions.
+ template <typename Proc, typename ClassT, typename... ArgTs>
+ static std::error_code
+ expect(ChannelT &C, ClassT &Instance,
+ std::error_code (ClassT::*HandlerMethod)(ArgTs...)) {
+ return expect<Proc>(
+ 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 Procedure<0, bool, int> Proc1;
+ ///
+ /// ...
+ /// bool B;
+ /// int I;
+ /// if (auto EC = expect<Proc1>(Channel, readArgs(B, I)))
+ /// /* Handle Args */ ;
+ ///
+ template <typename... ArgTs>
+ static ReadArgs<ArgTs...> readArgs(ArgTs &... Args) {
+ return ReadArgs<ArgTs...>(Args...);
+ }
+};
+
+} // end namespace remote
+} // end namespace orc
+} // end namespace llvm
+
+#endif
OpenPOWER on IntegriCloud