-
Notifications
You must be signed in to change notification settings - Fork 49
Expand file tree
/
Copy pathproxy.h
More file actions
338 lines (305 loc) · 14.4 KB
/
proxy.h
File metadata and controls
338 lines (305 loc) · 14.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
// Copyright (c) The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef MP_PROXY_H
#define MP_PROXY_H
#include <mp/util.h>
#include <cassert>
#include <functional>
#include <list>
#include <memory>
#include <stddef.h>
#include <tuple>
#include <type_traits>
#include <utility>
#include <variant> // IWYU pragma: keep
namespace mp {
class Connection;
class EventLoop;
//! Mapping from capnp interface type to proxy client implementation (specializations are generated by
//! proxy-codegen.cpp).
template <typename Interface> struct ProxyClient; // IWYU pragma: export
//! Mapping from capnp interface type to proxy server implementation (specializations are generated by
//! proxy-codegen.cpp).
template <typename Interface> struct ProxyServer; // IWYU pragma: export
//! Mapping from capnp method params type to method traits (specializations are generated by proxy-codegen.cpp).
template <typename Params> struct ProxyMethod; // IWYU pragma: export
//! Mapping from capnp struct type to struct traits (specializations are generated by proxy-codegen.cpp).
template <typename Struct> struct ProxyStruct; // IWYU pragma: export
//! Mapping from local c++ type to capnp type and traits (specializations are generated by proxy-codegen.cpp).
template <typename Type> struct ProxyType; // IWYU pragma: export
using CleanupList = std::list<std::function<void()>>;
using CleanupIt = typename CleanupList::iterator;
inline void CleanupRun(CleanupList& fns) {
while (!fns.empty()) {
auto fn = std::move(fns.front());
fns.pop_front();
fn();
}
}
//! Event loop smart pointer automatically managing m_num_clients.
//! If a lock pointer argument is passed, the specified lock will be used,
//! otherwise EventLoop::m_mutex will be locked when needed.
class EventLoopRef
{
public:
explicit EventLoopRef(EventLoop& loop, Lock* lock = nullptr);
EventLoopRef(EventLoopRef&& other) noexcept : m_loop(other.m_loop) { other.m_loop = nullptr; }
EventLoopRef(const EventLoopRef&) = delete;
EventLoopRef& operator=(const EventLoopRef&) = delete;
EventLoopRef& operator=(EventLoopRef&&) = delete;
~EventLoopRef() { reset(); }
EventLoop& operator*() const { assert(m_loop); return *m_loop; }
EventLoop* operator->() const { assert(m_loop); return m_loop; }
void reset(bool relock=false);
EventLoop* m_loop{nullptr};
Lock* m_lock{nullptr};
};
//! Context data associated with proxy client and server classes.
struct ProxyContext
{
Connection* connection;
EventLoopRef loop;
CleanupList cleanup_fns;
ProxyContext(Connection* connection);
};
//! Base class for generated ProxyClient classes that implement a C++ interface
//! and forward calls to a capnp interface.
template <typename Interface_, typename Impl_>
class ProxyClientBase : public Impl_
{
public:
using Interface = Interface_;
using Impl = Impl_;
using Sub = ProxyClient<Interface>;
using Super = ProxyClientBase<Interface, Impl>;
//! Construct libmultiprocess client object wrapping Cap'n Proto client
//! object with a reference to the associated mp::Connection object.
//!
//! The destroy_connection option determines whether destroying this client
//! object closes the connection. It is set to true for the
//! ProxyClient<InitInterface> object returned by ConnectStream, to let IPC
//! clients close the connection by freeing the object. It is false for
//! other client objects so they can be destroyed without affecting the
//! connection.
ProxyClientBase(typename Interface::Client client, Connection* connection, bool destroy_connection);
~ProxyClientBase() noexcept;
// construct/destroy methods called during client construction/destruction
// that can optionally be defined in capnp interfaces to invoke code on the
// server when proxy client objects are created and destroyed.
//
// The construct() method is not generally very useful, but can be used to
// run custom code on the server automatically when a ProxyClient client is
// constructed. The only current use is adding a construct method to Init
// interfaces that is called automatically on construction, so client and
// server exchange ThreadMap references and set Connection::m_thread_map
// values as soon as the Init client is created.
//
// construct @0 (threadMap: Proxy.ThreadMap) -> (threadMap: Proxy.ThreadMap);
//
// But construct() is not necessary for this, thread maps could be passed
// through a normal method that is just called explicitly rather than
// implicitly.
//
// The destroy() method is more generally useful than construct(), because
// it ensures that the server object will be destroyed synchronously before
// the client destructor returns, instead of asynchronously at some
// unpredictable time after the client object is already destroyed and
// client code has moved on. If the destroy method accepts a Context
// parameter like:
//
// destroy @0 (context: Proxy.Context) -> ();
//
// then it will also ensure that the destructor runs on the same thread the
// client used to make other RPC calls, instead of running on the server
// EventLoop thread and possibly blocking it.
static void construct(Super&) {}
static void destroy(Super&) {}
typename Interface::Client m_client;
ProxyContext m_context;
};
//! Customizable (through template specialization) base class used in generated ProxyClient implementations from
//! proxy-codegen.cpp.
template <typename Interface, typename Impl>
class ProxyClientCustom : public ProxyClientBase<Interface, Impl>
{
using ProxyClientBase<Interface, Impl>::ProxyClientBase;
};
//! Base class for generated ProxyServer classes that implement capnp server
//! methods and forward calls to a wrapped c++ implementation class.
template <typename Interface_, typename Impl_>
struct ProxyServerBase : public virtual Interface_::Server
{
public:
using Interface = Interface_;
using Impl = Impl_;
ProxyServerBase(std::shared_ptr<Impl> impl, Connection& connection);
virtual ~ProxyServerBase();
void invokeDestroy();
using Interface_::Server::thisCap;
/**
* Implementation pointer that may or may not be owned and deleted when this
* capnp server goes out of scope. It is owned for servers created to wrap
* unique_ptr<Impl> method arguments, but unowned for servers created to
* wrap Impl& method arguments.
*
* In the case of Impl& arguments, custom code is required on other side of
* the connection to delete the capnp client & server objects since native
* code on that side of the connection will just be taking a plain reference
* rather than a pointer, so won't be able to do its own cleanup. Right now
* this is implemented with addCloseHook callbacks to delete clients at
* appropriate times depending on semantics of the particular method being
* wrapped. */
std::shared_ptr<Impl> m_impl;
ProxyContext m_context;
};
//! Customizable (through template specialization) base class which ProxyServer
//! classes produced by generated code will inherit from. The default
//! specialization of this class just inherits from ProxyServerBase, but custom
//! specializations can be defined to control ProxyServer behavior.
//!
//! Specifically, it can be useful to specialize this class to add additional
//! state to ProxyServer classes, for example to cache state between IPC calls.
//! If this is done, however, care should be taken to ensure that the extra
//! state can be destroyed without blocking, because ProxyServer destructors are
//! called from the EventLoop thread, and if they block, it could deadlock the
//! program. One way to do avoid blocking is to clean up the state by pushing
//! cleanup callbacks to the m_context.cleanup_fns list, which run after the server
//! m_impl object is destroyed on the same thread destroying it (which will
//! either be an IPC worker thread if the ProxyServer is being explicitly
//! destroyed by a client calling a destroy() method with a Context argument and
//! Context.thread value set, or the temporary EventLoop::m_async_thread used to
//! run destructors without blocking the event loop when no-longer used server
//! objects are garbage collected by Cap'n Proto.) Alternately, if cleanup needs
//! to run before m_impl is destroyed, the specialization can override
//! invokeDestroy and destructor methods to do that.
template <typename Interface, typename Impl>
struct ProxyServerCustom : public ProxyServerBase<Interface, Impl>
{
using ProxyServerBase<Interface, Impl>::ProxyServerBase;
};
//! Function traits class used to get method parameter and result types, used in
//! generated ProxyClient and ProxyServer classes produced by gen.cpp to get C++
//! method type information. The generated code accesses these traits via
//! intermediate ProxyClientMethodTraits and ProxyServerMethodTraits classes,
//! which it is possible to specialize to change the way method arguments and
//! return values are handled.
//!
//! Fields of the trait class are:
//!
//! Params - TypeList of C++ ClassName::methodName parameter types
//! Result - Return type of ClassName::method
//! Param<N> - helper to access individual parameter types by index number.
//! Fwd<N> - helper to forward arguments by index number.
//! Fields - helper alias that appends Result type to the Params typelist if
//! it not void.
template <class Fn>
struct FunctionTraits;
//! Specialization of above extracting result and params types assuming the
//! template argument is a pointer-to-method type,
//! decltype(&ClassName::methodName)
template <class _Class, class _Result, class... _Params>
struct FunctionTraits<_Result (_Class::*const)(_Params...)>
{
using Params = TypeList<_Params...>;
using Result = _Result;
template <size_t N>
using Param = typename std::tuple_element<N, std::tuple<_Params...>>::type;
using Fields =
std::conditional_t<std::is_same_v<void, Result>, Params, TypeList<_Params..., _Result>>;
//! Enable perfect forwarding for clientInvoke calls. If parameter is a
//! value type or rvalue reference type, pass it as an rvalue-reference to
//! MakeClientParam and BuildField calls so it can be moved from, and if it
//! is an lvalue reference, pass it an lvalue reference so it won't be moved
//! from. This method does the same thing as std::forward except it takes a
//! parameter number instead of a type as a template argument, so generated
//! code calling this can be less repetitive and verbose.
template <size_t N>
static decltype(auto) Fwd(Param<N>& arg) { return static_cast<Param<N>&&>(arg); }
};
//! Traits class for a proxy method, providing the same
//! Params/Result/Param/Fields described in the FunctionTraits class above, plus
//! an additional invoke() method that calls the C++ method which is being
//! proxied, forwarding any arguments.
//!
//! The template argument should be the InterfaceName::MethodNameParams class
//! (generated by Cap'n Proto) associated with the method.
//!
//! Note: The class definition here is just the fallback definition used when
//! the other specialization below doesn't match. The fallback is only used for
//! capnp methods which do not have corresponding C++ methods, which in practice
//! is just the two special construct() and destroy() methods described in \ref
//! ProxyClientBase. These methods don't have any C++ parameters or return
//! types, so the trait information below reflects that.
template <typename MethodParams, typename Enable = void>
struct ProxyMethodTraits
{
using Params = TypeList<>;
using Result = void;
using Fields = Params;
template <typename ServerContext>
static void invoke(ServerContext&)
{
}
};
//! Specialization of above for proxy methods that have a
//! ProxyMethod<InterfaceName::MethodNameParams>::impl pointer-to-method
//! constant defined by generated code. This includes all functions defined in
//! the capnp interface except any construct() or destroy() methods, that are
//! assumed not to correspond to real member functions in the C++ class, and
//! will use the fallback traits definition above. The generated code this
//! specialization relies on looks like:
//!
//! struct ProxyMethod<InterfaceName::MethodNameParams>
//! {
//! static constexpr auto impl = &ClassName::methodName;
//! };
template <typename MethodParams>
struct ProxyMethodTraits<MethodParams, Require<decltype(ProxyMethod<MethodParams>::impl)>>
: public FunctionTraits<decltype(ProxyMethod<MethodParams>::impl)>
{
template <typename ServerContext, typename... Args>
static decltype(auto) invoke(ServerContext& server_context, Args&&... args)
{
return (server_context.proxy_server.m_impl.get()->*ProxyMethod<MethodParams>::impl)(std::forward<Args>(args)...);
}
};
//! Customizable (through template specialization) traits class used in generated ProxyClient implementations from
//! proxy-codegen.cpp.
template <typename MethodParams>
struct ProxyClientMethodTraits : public ProxyMethodTraits<MethodParams>
{
};
//! Customizable (through template specialization) traits class used in generated ProxyServer implementations from
//! proxy-codegen.cpp.
template <typename MethodParams>
struct ProxyServerMethodTraits : public ProxyMethodTraits<MethodParams>
{
};
static constexpr int FIELD_IN = 1;
static constexpr int FIELD_OUT = 2;
static constexpr int FIELD_OPTIONAL = 4;
static constexpr int FIELD_REQUESTED = 8;
static constexpr int FIELD_BOXED = 16;
//! Accessor type holding flags that determine how to access a message field.
template <typename Field, int flags>
struct Accessor : public Field
{
static constexpr bool in = (flags & FIELD_IN) != 0;
static constexpr bool out = (flags & FIELD_OUT) != 0;
static constexpr bool optional = (flags & FIELD_OPTIONAL) != 0;
static constexpr bool requested = (flags & FIELD_REQUESTED) != 0;
static constexpr bool boxed = (flags & FIELD_BOXED) != 0;
};
//! Wrapper around std::function for passing std::function objects between client and servers.
template <typename Fn>
class ProxyCallback;
//! Specialization of above to separate Result and Arg types.
template <typename Result, typename... Args>
class ProxyCallback<std::function<Result(Args...)>>
{
public:
virtual Result call(Args&&... args) = 0;
};
} // namespace mp
#endif // MP_PROXY_H