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