diff options
Diffstat (limited to 'include/llvm/ExecutionEngine/Orc/RPCUtils.h')
-rw-r--r-- | include/llvm/ExecutionEngine/Orc/RPCUtils.h | 266 |
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 |