Branch data Line data Source code
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 : 56 : inline void CleanupRun(CleanupList& fns) {
40 [ + + ]: 99 : while (!fns.empty()) {
41 : 43 : auto fn = std::move(fns.front());
42 : 43 : fns.pop_front();
43 [ + - ]: 43 : fn();
44 : 43 : }
45 : 56 : }
46 : :
47 : : //! Event loop smart pointer automatically managing m_num_clients.
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 [ + - ]: 109 : ~EventLoopRef() { reset(); }
59 [ - + ]: 70 : EventLoop& operator*() const { assert(m_loop); return *m_loop; }
60 [ - + ]: 455 : 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 : 37 : 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 : 13 : class ProxyClientCustom : public ProxyClientBase<Interface, Impl>
140 : : {
141 [ + - ][ + - : 13 : 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 : :
157 : : /**
158 : : * Implementation pointer that may or may not be owned and deleted when this
159 : : * capnp server goes out of scope. It is owned for servers created to wrap
160 : : * unique_ptr<Impl> method arguments, but unowned for servers created to
161 : : * wrap Impl& method arguments.
162 : : *
163 : : * In the case of Impl& arguments, custom code is required on other side of
164 : : * the connection to delete the capnp client & server objects since native
165 : : * code on that side of the connection will just be taking a plain reference
166 : : * rather than a pointer, so won't be able to do its own cleanup. Right now
167 : : * this is implemented with addCloseHook callbacks to delete clients at
168 : : * appropriate times depending on semantics of the particular method being
169 : : * wrapped. */
170 : : std::shared_ptr<Impl> m_impl;
171 : : ProxyContext m_context;
172 : : };
173 : :
174 : : //! Customizable (through template specialization) base class which ProxyServer
175 : : //! classes produced by generated code will inherit from. The default
176 : : //! specialization of this class just inherits from ProxyServerBase, but custom
177 : : //! specializations can be defined to control ProxyServer behavior.
178 : : //!
179 : : //! Specifically, it can be useful to specialize this class to add additional
180 : : //! state to ProxyServer classes, for example to cache state between IPC calls.
181 : : //! If this is done, however, care should be taken to ensure that the extra
182 : : //! state can be destroyed without blocking, because ProxyServer destructors are
183 : : //! called from the EventLoop thread, and if they block, it could deadlock the
184 : : //! program. One way to do avoid blocking is to clean up the state by pushing
185 : : //! cleanup callbacks to the m_context.cleanup_fns list, which run after the server
186 : : //! m_impl object is destroyed on the same thread destroying it (which will
187 : : //! either be an IPC worker thread if the ProxyServer is being explicitly
188 : : //! destroyed by a client calling a destroy() method with a Context argument and
189 : : //! Context.thread value set, or the temporary EventLoop::m_async_thread used to
190 : : //! run destructors without blocking the event loop when no-longer used server
191 : : //! objects are garbage collected by Cap'n Proto.) Alternately, if cleanup needs
192 : : //! to run before m_impl is destroyed, the specialization can override
193 : : //! invokeDestroy and destructor methods to do that.
194 : : template <typename Interface, typename Impl>
195 : : struct ProxyServerCustom : public ProxyServerBase<Interface, Impl>
196 : : {
197 : : using ProxyServerBase<Interface, Impl>::ProxyServerBase;
198 : : };
199 : :
200 : : //! Function traits class used to get method parameter and result types, used in
201 : : //! generated ProxyClient and ProxyServer classes produced by gen.cpp to get C++
202 : : //! method type information. The generated code accesses these traits via
203 : : //! intermediate ProxyClientMethodTraits and ProxyServerMethodTraits classes,
204 : : //! which it is possible to specialize to change the way method arguments and
205 : : //! return values are handled.
206 : : //!
207 : : //! Fields of the trait class are:
208 : : //!
209 : : //! Params - TypeList of C++ ClassName::methodName parameter types
210 : : //! Result - Return type of ClassName::method
211 : : //! Param<N> - helper to access individual parameter types by index number.
212 : : //! Fwd<N> - helper to forward arguments by index number.
213 : : //! Fields - helper alias that appends Result type to the Params typelist if
214 : : //! it not void.
215 : : template <class Fn>
216 : : struct FunctionTraits;
217 : :
218 : : //! Specialization of above extracting result and params types assuming the
219 : : //! template argument is a pointer-to-method type,
220 : : //! decltype(&ClassName::methodName)
221 : : template <class _Class, class _Result, class... _Params>
222 : : struct FunctionTraits<_Result (_Class::*const)(_Params...)>
223 : : {
224 : : using Params = TypeList<_Params...>;
225 : : using Result = _Result;
226 : : template <size_t N>
227 : : using Param = typename std::tuple_element<N, std::tuple<_Params...>>::type;
228 : : using Fields =
229 : : std::conditional_t<std::is_same_v<void, Result>, Params, TypeList<_Params..., _Result>>;
230 : :
231 : : //! Enable perfect forwarding for clientInvoke calls. If parameter is a
232 : : //! value type or rvalue reference type, pass it as an rvalue-reference to
233 : : //! MakeClientParam and BuildField calls so it can be moved from, and if it
234 : : //! is an lvalue reference, pass it an lvalue reference so it won't be moved
235 : : //! from. This method does the same thing as std::forward except it takes a
236 : : //! parameter number instead of a type as a template argument, so generated
237 : : //! code calling this can be less repetitive and verbose.
238 : : template <size_t N>
239 : : static decltype(auto) Fwd(Param<N>& arg) { return static_cast<Param<N>&&>(arg); }
240 : : };
241 : :
242 : : //! Traits class for a proxy method, providing the same
243 : : //! Params/Result/Param/Fields described in the FunctionTraits class above, plus
244 : : //! an additional invoke() method that calls the C++ method which is being
245 : : //! proxied, forwarding any arguments.
246 : : //!
247 : : //! The template argument should be the InterfaceName::MethodNameParams class
248 : : //! (generated by Cap'n Proto) associated with the method.
249 : : //!
250 : : //! Note: The class definition here is just the fallback definition used when
251 : : //! the other specialization below doesn't match. The fallback is only used for
252 : : //! capnp methods which do not have corresponding C++ methods, which in practice
253 : : //! is just the two special construct() and destroy() methods described in \ref
254 : : //! ProxyClientBase. These methods don't have any C++ parameters or return
255 : : //! types, so the trait information below reflects that.
256 : : template <typename MethodParams, typename Enable = void>
257 : : struct ProxyMethodTraits
258 : : {
259 : : using Params = TypeList<>;
260 : : using Result = void;
261 : : using Fields = Params;
262 : :
263 : : template <typename ServerContext>
264 : : static void invoke(ServerContext&)
265 : : {
266 : : }
267 : : };
268 : :
269 : : //! Specialization of above for proxy methods that have a
270 : : //! ProxyMethod<InterfaceName::MethodNameParams>::impl pointer-to-method
271 : : //! constant defined by generated code. This includes all functions defined in
272 : : //! the capnp interface except any construct() or destroy() methods, that are
273 : : //! assumed not to correspond to real member functions in the C++ class, and
274 : : //! will use the fallback traits definition above. The generated code this
275 : : //! specialization relies on looks like:
276 : : //!
277 : : //! struct ProxyMethod<InterfaceName::MethodNameParams>
278 : : //! {
279 : : //! static constexpr auto impl = &ClassName::methodName;
280 : : //! };
281 : : template <typename MethodParams>
282 : : struct ProxyMethodTraits<MethodParams, Require<decltype(ProxyMethod<MethodParams>::impl)>>
283 : : : public FunctionTraits<decltype(ProxyMethod<MethodParams>::impl)>
284 : : {
285 : : template <typename ServerContext, typename... Args>
286 : 18 : static decltype(auto) invoke(ServerContext& server_context, Args&&... args)
287 : : {
288 [ + - ][ # # : 18 : return (server_context.proxy_server.m_impl.get()->*ProxyMethod<MethodParams>::impl)(std::forward<Args>(args)...);
# # # # #
# # # #
# ]
[ # # # # ]
289 : : }
290 : : };
291 : :
292 : : //! Customizable (through template specialization) traits class used in generated ProxyClient implementations from
293 : : //! proxy-codegen.cpp.
294 : : template <typename MethodParams>
295 : : struct ProxyClientMethodTraits : public ProxyMethodTraits<MethodParams>
296 : : {
297 : : };
298 : :
299 : : //! Customizable (through template specialization) traits class used in generated ProxyServer implementations from
300 : : //! proxy-codegen.cpp.
301 : : template <typename MethodParams>
302 : : struct ProxyServerMethodTraits : public ProxyMethodTraits<MethodParams>
303 : : {
304 : : };
305 : :
306 : : static constexpr int FIELD_IN = 1;
307 : : static constexpr int FIELD_OUT = 2;
308 : : static constexpr int FIELD_OPTIONAL = 4;
309 : : static constexpr int FIELD_REQUESTED = 8;
310 : : static constexpr int FIELD_BOXED = 16;
311 : :
312 : : //! Accessor type holding flags that determine how to access a message field.
313 : : template <typename Field, int flags>
314 : : struct Accessor : public Field
315 : : {
316 : : static const bool in = flags & FIELD_IN;
317 : : static const bool out = flags & FIELD_OUT;
318 : : static const bool optional = flags & FIELD_OPTIONAL;
319 : : static const bool requested = flags & FIELD_REQUESTED;
320 : : static const bool boxed = flags & FIELD_BOXED;
321 : : };
322 : :
323 : : //! Wrapper around std::function for passing std::function objects between client and servers.
324 : : template <typename Fn>
325 : : class ProxyCallback;
326 : :
327 : : //! Specialization of above to separate Result and Arg types.
328 : : template <typename Result, typename... Args>
329 [ # # ]: 0 : class ProxyCallback<std::function<Result(Args...)>>
330 : : {
331 : : public:
332 : : virtual Result call(Args&&... args) = 0;
333 : : };
334 : :
335 : : } // namespace mp
336 : :
337 : : #endif // MP_PROXY_H
|