1 //===----- RPCUTils.h - Basic tilities for building RPC APIs ----*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // Basic utilities for building RPC APIs.
12 //===----------------------------------------------------------------------===//
14 #ifndef LLVM_EXECUTIONENGINE_ORC_RPCUTILS_H
15 #define LLVM_EXECUTIONENGINE_ORC_RPCUTILS_H
17 #include "llvm/ADT/STLExtras.h"
23 // Base class containing utilities that require partial specialization.
24 // These cannot be included in RPC, as template class members cannot be
25 // partially specialized.
28 template <typename ProcedureIdT, ProcedureIdT ProcId, typename... Ts>
29 class ProcedureHelper {
31 static const ProcedureIdT Id = ProcId;
34 template <typename ChannelT, typename Proc> class CallHelper;
36 template <typename ChannelT, typename ProcedureIdT, ProcedureIdT ProcId,
38 class CallHelper<ChannelT, ProcedureHelper<ProcedureIdT, ProcId, ArgTs...>> {
40 static std::error_code call(ChannelT &C, const ArgTs &... Args) {
41 if (auto EC = serialize(C, ProcId))
43 // If you see a compile-error on this line you're probably calling a
44 // function with the wrong signature.
45 return serialize_seq(C, Args...);
49 template <typename ChannelT, typename Proc> class HandlerHelper;
51 template <typename ChannelT, typename ProcedureIdT, ProcedureIdT ProcId,
53 class HandlerHelper<ChannelT,
54 ProcedureHelper<ProcedureIdT, ProcId, ArgTs...>> {
56 template <typename HandlerT>
57 static std::error_code handle(ChannelT &C, HandlerT Handler) {
58 return readAndHandle(C, Handler, llvm::index_sequence_for<ArgTs...>());
62 template <typename HandlerT, size_t... Is>
63 static std::error_code readAndHandle(ChannelT &C, HandlerT Handler,
64 llvm::index_sequence<Is...> _) {
65 std::tuple<ArgTs...> RPCArgs;
66 if (auto EC = deserialize_seq(C, std::get<Is>(RPCArgs)...))
68 return Handler(std::get<Is>(RPCArgs)...);
72 template <typename... ArgTs> class ReadArgs {
74 std::error_code operator()() { return std::error_code(); }
77 template <typename ArgT, typename... ArgTs>
78 class ReadArgs<ArgT, ArgTs...> : public ReadArgs<ArgTs...> {
80 ReadArgs(ArgT &Arg, ArgTs &... Args)
81 : ReadArgs<ArgTs...>(Args...), Arg(Arg) {}
83 std::error_code operator()(ArgT &ArgVal, ArgTs &... ArgVals) {
84 this->Arg = std::move(ArgVal);
85 return ReadArgs<ArgTs...>::operator()(ArgVals...);
93 /// Contains primitive utilities for defining, calling and handling calls to
94 /// remote procedures. ChannelT is a bidirectional stream conforming to the
95 /// RPCChannel interface (see RPCChannel.h), and ProcedureIdT is a procedure
96 /// identifier type that must be serializable on ChannelT.
98 /// These utilities support the construction of very primitive RPC utilities.
99 /// Their intent is to ensure correct serialization and deserialization of
100 /// procedure arguments, and to keep the client and server's view of the API in
103 /// These utilities do not support return values. These can be handled by
104 /// declaring a corresponding '.*Response' procedure and expecting it after a
105 /// call). They also do not support versioning: the client and server *must* be
106 /// compiled with the same procedure definitions.
110 /// Overview (see comments individual types/methods for details):
112 /// Procedure<Id, Args...> :
114 /// associates a unique serializable id with an argument list.
117 /// call<Proc>(Channel, Args...) :
119 /// Calls the remote procedure 'Proc' by serializing Proc's id followed by its
120 /// arguments and sending the resulting bytes to 'Channel'.
123 /// handle<Proc>(Channel, <functor matching std::error_code(Args...)> :
125 /// Handles a call to 'Proc' by deserializing its arguments and calling the
126 /// given functor. This assumes that the id for 'Proc' has already been
129 /// expect<Proc>(Channel, <functor matching std::error_code(Args...)> :
131 /// The same as 'handle', except that the procedure id should not have been
132 /// read yet. Expect will deserialize the id and assert that it matches Proc's
133 /// id. If it does not, and unexpected RPC call error is returned.
135 template <typename ChannelT, typename ProcedureIdT = uint32_t>
136 class RPC : public RPCBase {
138 /// Utility class for defining/referring to RPC procedures.
140 /// Typedefs of this utility are used when calling/handling remote procedures.
142 /// ProcId should be a unique value of ProcedureIdT (i.e. not used with any
143 /// other Procedure typedef in the RPC API being defined.
145 /// the template argument Ts... gives the argument list for the remote
150 /// typedef Procedure<0, bool> Proc1;
151 /// typedef Procedure<1, std::string, std::vector<int>> Proc2;
153 /// if (auto EC = call<Proc1>(Channel, true))
156 /// if (auto EC = expect<Proc2>(Channel,
157 /// [](std::string &S, std::vector<int> &V) {
159 /// return std::error_code();
163 template <ProcedureIdT ProcId, typename... Ts>
164 using Procedure = ProcedureHelper<ProcedureIdT, ProcId, Ts...>;
166 /// Serialize Args... to channel C, but do not call C.send().
168 /// For buffered channels, this can be used to queue up several calls before
169 /// flushing the channel.
170 template <typename Proc, typename... ArgTs>
171 static std::error_code appendCall(ChannelT &C, const ArgTs &... Args) {
172 return CallHelper<ChannelT, Proc>::call(C, Args...);
175 /// Serialize Args... to channel C and call C.send().
176 template <typename Proc, typename... ArgTs>
177 static std::error_code call(ChannelT &C, const ArgTs &... Args) {
178 if (auto EC = appendCall<Proc>(C, Args...))
183 /// Deserialize and return an enum whose underlying type is ProcedureIdT.
184 static std::error_code getNextProcId(ChannelT &C, ProcedureIdT &Id) {
185 return deserialize(C, Id);
188 /// Deserialize args for Proc from C and call Handler. The signature of
189 /// handler must conform to 'std::error_code(Args...)' where Args... matches
190 /// the arguments used in the Proc typedef.
191 template <typename Proc, typename HandlerT>
192 static std::error_code handle(ChannelT &C, HandlerT Handler) {
193 return HandlerHelper<ChannelT, Proc>::handle(C, Handler);
196 /// Deserialize a ProcedureIdT from C and verify it matches the id for Proc.
197 /// If the id does match, deserialize the arguments and call the handler
198 /// (similarly to handle).
199 /// If the id does not match, return an unexpect RPC call error and do not
200 /// deserialize any further bytes.
201 template <typename Proc, typename HandlerT>
202 static std::error_code expect(ChannelT &C, HandlerT Handler) {
204 if (auto EC = getNextProcId(C, ProcId))
206 if (ProcId != Proc::Id)
207 return orcError(OrcErrorCode::UnexpectedRPCCall);
208 return handle<Proc>(C, Handler);
211 /// Helper for handling setter procedures - this method returns a functor that
212 /// sets the variables referred to by Args... to values deserialized from the
216 /// typedef Procedure<0, bool, int> Proc1;
221 /// if (auto EC = expect<Proc1>(Channel, readArgs(B, I)))
222 /// /* Handle Args */ ;
224 template <typename... ArgTs>
225 static ReadArgs<ArgTs...> readArgs(ArgTs &... Args) {
226 return ReadArgs<ArgTs...>(Args...);
230 } // end namespace remote
231 } // end namespace orc
232 } // end namespace llvm