proxy.h raw

   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