| //===----- 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 |