//===----- 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 class ProcedureHelper { public: static const ProcedureIdT Id = ProcId; }; template class CallHelper; template class CallHelper> { 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 class HandlerHelper; template class HandlerHelper> { public: template static std::error_code handle(ChannelT &C, HandlerT Handler) { return readAndHandle(C, Handler, llvm::index_sequence_for()); } private: template static std::error_code readAndHandle(ChannelT &C, HandlerT Handler, llvm::index_sequence _) { std::tuple RPCArgs; if (auto EC = deserialize_seq(C, std::get(RPCArgs)...)) return EC; return Handler(std::get(RPCArgs)...); } }; template 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 class ReadArgs { public: std::error_code operator()() { return std::error_code(); } }; template class ReadArgs : public ReadArgs { public: ReadArgs(ArgT &Arg, ArgTs &... Args) : ReadArgs(Args...), Arg(Arg) {} std::error_code operator()(ArgT &ArgVal, ArgTs &... ArgVals) { this->Arg = std::move(ArgVal); return ReadArgs::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 : /// /// associates a unique serializable id with an argument list. /// /// /// call(Channel, Args...) : /// /// Calls the remote procedure 'Proc' by serializing Proc's id followed by its /// arguments and sending the resulting bytes to 'Channel'. /// /// /// handle(Channel, : /// /// 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(Channel, : /// /// 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 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> Proc2; /// /// if (auto EC = call(Channel, true)) /// /* handle EC */; /// /// if (auto EC = expect(Channel, /// [](std::string &S, std::vector &V) { /// // Stuff. /// return std::error_code(); /// }) /// /* handle EC */; /// template using Procedure = ProcedureHelper; /// 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 static std::error_code appendCall(ChannelT &C, const ArgTs &... Args) { return CallHelper::call(C, Args...); } /// Serialize Args... to channel C and call C.send(). template static std::error_code call(ChannelT &C, const ArgTs &... Args) { if (auto EC = appendCall(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 static std::error_code handle(ChannelT &C, HandlerT Handler) { return HandlerHelper::handle(C, Handler); } /// Helper version of 'handle' for calling member functions. template static std::error_code handle(ChannelT &C, ClassT &Instance, std::error_code (ClassT::*HandlerMethod)(ArgTs...)) { return handle( C, MemberFnWrapper(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 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(C, Handler); } /// Helper version of expect for calling member functions. template static std::error_code expect(ChannelT &C, ClassT &Instance, std::error_code (ClassT::*HandlerMethod)(ArgTs...)) { return expect( C, MemberFnWrapper(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(Channel, readArgs(B, I))) /// /* Handle Args */ ; /// template static ReadArgs readArgs(ArgTs &... Args) { return ReadArgs(Args...); } }; } // end namespace remote } // end namespace orc } // end namespace llvm #endif