// Copyright (c) 2019 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 #include #include #include #include #include #include #include namespace mp { class Connection; class EventLoop; //! Mapping from capnp interface type to proxy client implementation (specializations are generated by //! proxy-codegen.cpp). template struct ProxyClient; //! Mapping from capnp interface type to proxy server implementation (specializations are generated by //! proxy-codegen.cpp). template struct ProxyServer; //! Mapping from capnp method params type to method traits (specializations are generated by proxy-codegen.cpp). template struct ProxyMethod; //! Mapping from capnp struct type to struct traits (specializations are generated by proxy-codegen.cpp). template struct ProxyStruct; //! Mapping from local c++ type to capnp type and traits (specializations are generated by proxy-codegen.cpp). template struct ProxyType; using CleanupList = std::list>; using CleanupIt = typename CleanupList::iterator; inline void CleanupRun(CleanupList& fns) { while (!fns.empty()) { auto fn = std::move(fns.front()); fns.pop_front(); fn(); } } //! Context data associated with proxy client and server classes. struct ProxyContext { Connection* connection; CleanupList cleanup_fns; ProxyContext(Connection* connection) : connection(connection) {} }; //! Base class for generated ProxyClient classes that implement a C++ interface //! and forward calls to a capnp interface. template class ProxyClientBase : public Impl_ { public: using Interface = Interface_; using Impl = Impl_; using Sub = ProxyClient; using Super = ProxyClientBase; 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 class ProxyClientCustom : public ProxyClientBase { using ProxyClientBase::ProxyClientBase; }; //! Base class for generated ProxyServer classes that implement capnp server //! methods and forward calls to a wrapped c++ implementation class. template struct ProxyServerBase : public virtual Interface_::Server { public: using Interface = Interface_; using Impl = Impl_; ProxyServerBase(std::shared_ptr impl, Connection& connection); virtual ~ProxyServerBase(); void invokeDestroy(); /** * 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 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 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 struct ProxyServerCustom : public ProxyServerBase { using ProxyServerBase::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 - helper to access individual parameters by index number. //! Fields - helper alias that appends Result type to the Params typelist if //! it not void. template struct FunctionTraits; //! Specialization of above extracting result and params types assuming the //! template argument is a pointer-to-method type, //! decltype(&ClassName::methodName) template struct FunctionTraits<_Result (_Class::*const)(_Params...)> { using Params = TypeList<_Params...>; using Result = _Result; template using Param = typename std::tuple_element>::type; using Fields = std::conditional_t, Params, TypeList<_Params..., _Result>>; }; //! 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 struct ProxyMethodTraits { using Params = TypeList<>; using Result = void; using Fields = Params; template static void invoke(ServerContext&) { } }; //! Specialization of above for proxy methods that have a //! ProxyMethod::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 //! { //! static constexpr auto impl = &ClassName::methodName; //! }; template struct ProxyMethodTraits::impl)>> : public FunctionTraits::impl)> { template static decltype(auto) invoke(ServerContext& server_context, Args&&... args) { return (server_context.proxy_server.m_impl.get()->*ProxyMethod::impl)(std::forward(args)...); } }; //! Customizable (through template specialization) traits class used in generated ProxyClient implementations from //! proxy-codegen.cpp. template struct ProxyClientMethodTraits : public ProxyMethodTraits { }; //! Customizable (through template specialization) traits class used in generated ProxyServer implementations from //! proxy-codegen.cpp. template struct ProxyServerMethodTraits : public ProxyMethodTraits { }; 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 struct Accessor : public Field { static const bool in = flags & FIELD_IN; static const bool out = flags & FIELD_OUT; static const bool optional = flags & FIELD_OPTIONAL; static const bool requested = flags & FIELD_REQUESTED; static const bool boxed = flags & FIELD_BOXED; }; //! Wrapper around std::function for passing std::function objects between client and servers. template class ProxyCallback; //! Specialization of above to separate Result and Arg types. template class ProxyCallback> { public: virtual Result call(Args&&... args) = 0; }; } // namespace mp #endif // MP_PROXY_H