Bitcoin Core  31.0.0
P2P Digital Currency
proxy-types.h
Go to the documentation of this file.
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_TYPES_H
6 #define MP_PROXY_TYPES_H
7 
8 #include <mp/proxy-io.h>
9 
10 #include <exception>
11 #include <optional>
12 #include <set>
13 #include <typeindex>
14 #include <vector>
15 
16 namespace mp {
17 
18 template <typename Value>
20 {
21 public:
22  ValueField(Value& value) : m_value(value) {}
23  ValueField(Value&& value) : m_value(value) {}
24  Value& m_value;
25 
26  const Value& get() const { return m_value; }
27  Value& get() { return m_value; }
28  Value& init() { return m_value; }
29  bool has() const { return true; }
30 };
31 
32 template <typename Accessor, typename Struct>
34 {
35  template <typename S>
36  StructField(S& struct_) : m_struct(struct_)
37  {
38  }
39  Struct& m_struct;
40 
41  decltype(auto) get() const { return Accessor::get(this->m_struct); }
42 
43  bool has() const {
44  if constexpr (Accessor::optional) {
45  return Accessor::getHas(m_struct);
46  } else if constexpr (Accessor::boxed) {
47  return Accessor::has(m_struct);
48  } else {
49  return true;
50  }
51  }
52 
53  bool want() const {
54  if constexpr (Accessor::requested) {
55  return Accessor::getWant(m_struct);
56  } else {
57  return true;
58  }
59  }
60 
61  template <typename... Args> decltype(auto) set(Args &&...args) const {
62  return Accessor::set(this->m_struct, std::forward<Args>(args)...);
63  }
64 
65  template <typename... Args> decltype(auto) init(Args &&...args) const {
66  return Accessor::init(this->m_struct, std::forward<Args>(args)...);
67  }
68 
69  void setHas() const {
70  if constexpr (Accessor::optional) {
71  Accessor::setHas(m_struct);
72  }
73  }
74 
75  void setWant() const {
76  if constexpr (Accessor::requested) {
77  Accessor::setWant(m_struct);
78  }
79  }
80 };
81 
82 
83 
84 // Destination parameter type that can be passed to ReadField function as an
85 // alternative to ReadDestUpdate. It allows the ReadField implementation to call
86 // the provided emplace_fn function with constructor arguments, so it only needs
87 // to determine the arguments, and can let the emplace function decide how to
88 // actually construct the read destination object. For example, if a std::string
89 // is being read, the ReadField call will call the custom emplace_fn with char*
90 // and size_t arguments, and the emplace function can decide whether to call the
91 // constructor via the operator, make_shared, emplace or just return a
92 // temporary string that is moved from.
93 template <typename LocalType, typename EmplaceFn>
95 {
96  ReadDestEmplace(TypeList<LocalType>, EmplaceFn emplace_fn) : m_emplace_fn(std::move(emplace_fn)) {}
97 
100  template <typename... Args>
101  decltype(auto) construct(Args&&... args)
102  {
103  return m_emplace_fn(std::forward<Args>(args)...);
104  }
105 
110  template <typename UpdateFn>
111  decltype(auto) update(UpdateFn&& update_fn)
112  {
113  if constexpr (std::is_const_v<std::remove_reference_t<std::invoke_result_t<EmplaceFn>>>) {
114  // If destination type is const, default construct temporary
115  // to pass to update, then call move constructor via construct() to
116  // move from that temporary.
117  std::remove_cv_t<LocalType> temp;
118  update_fn(temp);
119  return construct(std::move(temp));
120  } else {
121  // Default construct object and pass it to update_fn.
122  decltype(auto) temp = construct();
123  update_fn(temp);
124  return temp;
125  }
126  }
127  EmplaceFn m_emplace_fn;
128 };
129 
132 template <typename LocalType>
134 {
135  return ReadDestEmplace{TypeList<LocalType>(), [](auto&&... args) -> decltype(auto) {
136  return LocalType{std::forward<decltype(args)>(args)...};
137  }};
138 }
139 
144 template <typename Value>
146 {
147  ReadDestUpdate(Value& value) : m_value(value) {}
148 
150  template <typename UpdateFn>
151  Value& update(UpdateFn&& update_fn)
152  {
153  update_fn(m_value);
154  return m_value;
155  }
156 
159  template <typename... Args>
160  Value& construct(Args&&... args)
161  {
162  m_value.~Value();
163  new (&m_value) Value(std::forward<Args>(args)...);
164  return m_value;
165  }
166 
167  Value& m_value;
168 };
169 
206 template <typename... LocalTypes, typename Input>
207 bool CustomHasField(TypeList<LocalTypes...>, InvokeContext& invoke_context, const Input& input)
208 {
209  return input.has();
210 }
211 
212 template <typename... LocalTypes, typename Input, typename... Args>
213 decltype(auto) ReadField(TypeList<LocalTypes...>, InvokeContext& invoke_context, Input&& input, Args&&... args)
214 {
215  return CustomReadField(TypeList<RemoveCvRef<LocalTypes>...>(), Priority<2>(), invoke_context, std::forward<Input>(input), std::forward<Args>(args)...);
216 }
217 
218 template <typename LocalType, typename Input>
219 void ThrowField(TypeList<LocalType>, InvokeContext& invoke_context, Input&& input)
220 {
221  ReadField(
222  TypeList<LocalType>(), invoke_context, input, ReadDestEmplace(TypeList<LocalType>(),
223  [](auto&& ...args) -> const LocalType& { throw LocalType{std::forward<decltype(args)>(args)...}; }));
224 }
225 
229 template <typename Input>
230 void ThrowField(TypeList<std::exception>, InvokeContext& invoke_context, Input&& input)
231 {
232  auto data = input.get();
233  throw std::runtime_error(std::string(CharCast(data.begin()), data.size()));
234 }
235 
243 template <typename... Values>
244 bool CustomHasValue(InvokeContext& invoke_context, const Values&... value)
245 {
246  return true;
247 }
248 
249 template <typename... LocalTypes, typename Context, typename... Values, typename Output>
250 void BuildField(TypeList<LocalTypes...>, Context& context, Output&& output, Values&&... values)
251 {
252  if (CustomHasValue(context, values...)) {
253  CustomBuildField(TypeList<LocalTypes...>(), Priority<3>(), context, std::forward<Values>(values)...,
254  std::forward<Output>(output));
255  }
256 }
257 
258 // Adapter that allows BuildField overloads to work with, set, and initialize list
259 // elements as if they were fields of a struct. If BuildField is changed to use some
260 // kind of accessor class instead of calling method pointers, then maybe this could
261 // go away or be simplified, because there would no longer be a need to return
262 // ListOutput method pointers emulating capnp struct method pointers.
263 template <typename ListType>
264 struct ListOutput;
265 
266 template <typename T, ::capnp::Kind kind>
267 struct ListOutput<::capnp::List<T, kind>>
268 {
269  using Builder = typename ::capnp::List<T, kind>::Builder;
270 
271  ListOutput(Builder& builder, size_t index) : m_builder(builder), m_index(index) {}
273  size_t m_index;
274 
275  // clang-format off
276  decltype(auto) get() const { return this->m_builder[this->m_index]; }
277  decltype(auto) init() const { return this->m_builder[this->m_index]; }
278  template<typename B = Builder, typename Arg> decltype(auto) set(Arg&& arg) const { return static_cast<B&>(this->m_builder).set(m_index, std::forward<Arg>(arg)); }
279  template<typename B = Builder, typename Arg> decltype(auto) init(Arg&& arg) const { return static_cast<B&>(this->m_builder).init(m_index, std::forward<Arg>(arg)); }
280  // clang-format on
281 };
282 
283 template <typename LocalType, typename Value, typename Output>
284 void CustomBuildField(TypeList<LocalType>, Priority<0>, InvokeContext& invoke_context, Value&& value, Output&& output)
285 {
286  output.set(BuildPrimitive(invoke_context, std::forward<Value>(value), TypeList<decltype(output.get())>()));
287 }
288 
290 template <typename Accessor, typename LocalType, typename ServerContext, typename Fn, typename... Args>
291 auto PassField(Priority<1>, TypeList<LocalType&>, ServerContext& server_context, Fn&& fn, Args&&... args)
292  -> Require<typename decltype(Accessor::get(server_context.call_context.getParams()))::Calls>
293 {
294  // Just create a temporary ProxyClient if argument is a reference to an
295  // interface client. If argument needs to have a longer lifetime and not be
296  // destroyed after this call, a CustomPassField overload can be implemented
297  // to bypass this code, and a custom ProxyServerMethodTraits overload can be
298  // implemented in order to read the capability pointer out of params and
299  // construct a ProxyClient with a longer lifetime.
300  const auto& params = server_context.call_context.getParams();
301  const auto& input = Make<StructField, Accessor>(params);
302  using Interface = typename Decay<decltype(input.get())>::Calls;
303  auto param = std::make_unique<ProxyClient<Interface>>(input.get(), server_context.proxy_server.m_context.connection, false);
304  fn.invoke(server_context, std::forward<Args>(args)..., *param);
305 }
306 
307 template <typename... Args>
308 void MaybeBuildField(std::true_type, Args&&... args)
309 {
310  BuildField(std::forward<Args>(args)...);
311 }
312 template <typename... Args>
313 void MaybeBuildField(std::false_type, Args&&...)
314 {
315 }
316 template <typename... Args>
317 void MaybeReadField(std::true_type, Args&&... args)
318 {
319  ReadField(std::forward<Args>(args)...);
320 }
321 template <typename... Args>
322 void MaybeReadField(std::false_type, Args&&...)
323 {
324 }
325 
326 template <typename LocalType, typename Value, typename Output>
327 void MaybeSetWant(TypeList<LocalType*>, Priority<1>, const Value& value, Output&& output)
328 {
329  if (value) {
330  output.setWant();
331  }
332 }
333 
334 template <typename LocalTypes, typename... Args>
335 void MaybeSetWant(LocalTypes, Priority<0>, const Args&...)
336 {
337 }
338 
340 template <typename Accessor, typename LocalType, typename ServerContext, typename Fn, typename... Args>
341 void PassField(Priority<0>, TypeList<LocalType>, ServerContext& server_context, Fn&& fn, Args&&... args)
342 {
343  InvokeContext& invoke_context = server_context;
344  using ArgType = RemoveCvRef<LocalType>;
345  std::optional<ArgType> param;
346  const auto& params = server_context.call_context.getParams();
347  MaybeReadField(std::integral_constant<bool, Accessor::in>(), TypeList<ArgType>(), invoke_context,
348  Make<StructField, Accessor>(params), ReadDestEmplace(TypeList<ArgType>(), [&](auto&&... args) -> auto& {
349  param.emplace(std::forward<decltype(args)>(args)...);
350  return *param;
351  }));
352  if constexpr (Accessor::in) {
353  assert(param);
354  } else {
355  if (!param) param.emplace();
356  }
357  fn.invoke(server_context, std::forward<Args>(args)..., static_cast<LocalType&&>(*param));
358  auto&& results = server_context.call_context.getResults();
359  MaybeBuildField(std::integral_constant<bool, Accessor::out>(), TypeList<LocalType>(), invoke_context,
360  Make<StructField, Accessor>(results), *param);
361 }
362 
364 template <typename Accessor, typename ServerContext, typename Fn, typename... Args>
365 void PassField(Priority<0>, TypeList<>, ServerContext& server_context, const Fn& fn, Args&&... args)
366 {
367  const auto& params = server_context.call_context.getParams();
368  const auto& input = Make<StructField, Accessor>(params);
369  ReadField(TypeList<>(), server_context, input);
370  fn.invoke(server_context, std::forward<Args>(args)...);
371  auto&& results = server_context.call_context.getResults();
372  BuildField(TypeList<>(), server_context, Make<StructField, Accessor>(results));
373 }
374 
375 template <typename Derived, size_t N = 0>
377 {
378  template <typename Arg1, typename Arg2, typename ParamList, typename NextFn, typename... NextFnArgs>
379  void handleChain(Arg1& arg1, Arg2& arg2, ParamList, NextFn&& next_fn, NextFnArgs&&... next_fn_args)
380  {
381  using S = Split<N, ParamList>;
382  handleChain(arg1, arg2, typename S::First());
383  next_fn.handleChain(arg1, arg2, typename S::Second(),
384  std::forward<NextFnArgs>(next_fn_args)...);
385  }
386 
387  template <typename Arg1, typename Arg2, typename ParamList>
388  void handleChain(Arg1& arg1, Arg2& arg2, ParamList)
389  {
390  static_cast<Derived*>(this)->handleField(arg1, arg2, ParamList());
391  }
392 private:
393  IterateFieldsHelper() = default;
394  friend Derived;
395 };
396 
397 struct IterateFields : IterateFieldsHelper<IterateFields, 0>
398 {
399  template <typename Arg1, typename Arg2, typename ParamList>
400  void handleField(Arg1&&, Arg2&&, ParamList)
401  {
402  }
403 };
404 
405 template <typename Exception, typename Accessor>
407 {
408  struct BuildParams : IterateFieldsHelper<BuildParams, 0>
409  {
410  template <typename Params, typename ParamList>
411  void handleField(InvokeContext& invoke_context, Params& params, ParamList)
412  {
413  }
414 
415  BuildParams(ClientException* client_exception) : m_client_exception(client_exception) {}
417  };
418 
419  struct ReadResults : IterateFieldsHelper<ReadResults, 0>
420  {
421  template <typename Results, typename ParamList>
422  void handleField(InvokeContext& invoke_context, Results& results, ParamList)
423  {
424  StructField<Accessor, Results> input(results);
425  if (CustomHasField(TypeList<Exception>(), invoke_context, input)) {
426  ThrowField(TypeList<Exception>(), invoke_context, input);
427  }
428  }
429 
430  ReadResults(ClientException* client_exception) : m_client_exception(client_exception) {}
432  };
433 };
434 
435 template <typename Accessor, typename... Types>
437 {
438  ClientParam(Types&&... values) : m_values{std::forward<Types>(values)...} {}
439 
440  struct BuildParams : IterateFieldsHelper<BuildParams, sizeof...(Types)>
441  {
442  template <typename Params, typename ParamList>
443  void handleField(ClientInvokeContext& invoke_context, Params& params, ParamList)
444  {
445  auto const fun = [&]<typename... Values>(Values&&... values) {
446  MaybeSetWant(
447  ParamList(), Priority<1>(), values..., Make<StructField, Accessor>(params));
448  MaybeBuildField(std::integral_constant<bool, Accessor::in>(), ParamList(), invoke_context,
449  Make<StructField, Accessor>(params), std::forward<Values>(values)...);
450  };
451 
452  // Note: The m_values tuple just consists of lvalue and rvalue
453  // references, so calling std::move doesn't change the tuple, it
454  // just causes std::apply to call the std::get overload that returns
455  // && instead of &, so rvalue references are preserved and not
456  // turned into lvalue references. This allows the BuildField call to
457  // move from the argument if it is an rvalue reference or was passed
458  // by value.
459  std::apply(fun, std::move(m_client_param->m_values));
460  }
461 
462  BuildParams(ClientParam* client_param) : m_client_param(client_param) {}
464  };
465 
466  struct ReadResults : IterateFieldsHelper<ReadResults, sizeof...(Types)>
467  {
468  template <typename Results, typename... Params>
469  void handleField(ClientInvokeContext& invoke_context, Results& results, TypeList<Params...>)
470  {
471  auto const fun = [&]<typename... Values>(Values&&... values) {
472  MaybeReadField(std::integral_constant<bool, Accessor::out>(), TypeList<Decay<Params>...>(), invoke_context,
473  Make<StructField, Accessor>(results), ReadDestUpdate(values)...);
474  };
475 
476  std::apply(fun, m_client_param->m_values);
477  }
478 
479  ReadResults(ClientParam* client_param) : m_client_param(client_param) {}
481  };
482 
483  std::tuple<Types&&...> m_values;
484 };
485 
486 template <typename Accessor, typename... Types>
488 {
489  return {std::forward<Types>(values)...};
490 }
491 
493 {
494  // FIXME: maybe call call_context.releaseParams()
495  template <typename ServerContext, typename... Args>
496  decltype(auto) invoke(ServerContext& server_context, TypeList<>, Args&&... args) const
497  {
498  // If cancel_lock is set, release it while executing the method, and
499  // reacquire it afterwards. The lock is needed to prevent params and
500  // response structs from being deleted by the event loop thread if the
501  // request is canceled, so it is only needed before and after method
502  // execution. It is important to release the lock during execution
503  // because the method can take arbitrarily long to return and the event
504  // loop will need the lock itself in on_cancel if the call is canceled.
505  if (server_context.cancel_lock) server_context.cancel_lock->m_lock.unlock();
506  return TryFinally(
507  [&]() -> decltype(auto) {
509  typename decltype(server_context.call_context.getParams())::Reads
510  >::invoke(server_context, std::forward<Args>(args)...);
511  },
512  [&] {
513  if (server_context.cancel_lock) server_context.cancel_lock->m_lock.lock();
514  // If the IPC request was canceled, throw InterruptException
515  // because there is no point continuing and trying to fill the
516  // call_context.getResults() struct. It's also important to stop
517  // executing because the connection may have been destroyed as
518  // described in https://github.com/bitcoin/bitcoin/issues/34250
519  // and there could be invalid references to the destroyed
520  // Connection object if this continued.
521  // If the IPC method itself threw an exception, the
522  // InterruptException thrown below will take precedence over it.
523  // Since the call has been canceled that exception can't be
524  // returned to the caller, so it needs to be discarded like
525  // other result values.
526  if (server_context.request_canceled) throw InterruptException{"canceled"};
527  });
528  }
529 };
530 
532 {
533  template <typename ServerContext, typename... Args>
534  void invoke(ServerContext& server_context, TypeList<>, Args&&... args) const
535  {
536  server_context.proxy_server.invokeDestroy(std::forward<Args>(args)...);
537  }
538 };
539 
540 template <typename Accessor, typename Parent>
541 struct ServerRet : Parent
542 {
543  ServerRet(Parent parent) : Parent(parent) {}
544 
545  template <typename ServerContext, typename... Args>
546  void invoke(ServerContext& server_context, TypeList<>, Args&&... args) const
547  {
548  auto&& result = Parent::invoke(server_context, TypeList<>(), std::forward<Args>(args)...);
549  auto&& results = server_context.call_context.getResults();
550  InvokeContext& invoke_context = server_context;
551  BuildField(TypeList<decltype(result)>(), invoke_context, Make<StructField, Accessor>(results),
552  std::forward<decltype(result)>(result));
553  }
554 };
555 
556 template <typename Exception, typename Accessor, typename Parent>
557 struct ServerExcept : Parent
558 {
559  ServerExcept(Parent parent) : Parent(parent) {}
560 
561  template <typename ServerContext, typename... Args>
562  void invoke(ServerContext& server_context, TypeList<>, Args&&... args) const
563  {
564  try {
565  return Parent::invoke(server_context, TypeList<>(), std::forward<Args>(args)...);
566  } catch (const Exception& exception) {
567  auto&& results = server_context.call_context.getResults();
568  BuildField(TypeList<Exception>(), server_context, Make<StructField, Accessor>(results), exception);
569  }
570  }
571 };
572 
575 template <typename Accessor, typename Message>
576 decltype(auto) MaybeGet(Message&& message, decltype(Accessor::get(message))* enable = nullptr)
577 {
578  return Accessor::get(message);
579 }
580 
581 template <typename Accessor>
582 ::capnp::Void MaybeGet(...)
583 {
584  return {};
585 }
586 
587 template <class Accessor>
588 void CustomPassField();
589 
600 template <typename Accessor, typename... Args>
601 auto PassField(Priority<2>, Args&&... args) -> decltype(CustomPassField<Accessor>(std::forward<Args>(args)...))
602 {
603  return CustomPassField<Accessor>(std::forward<Args>(args)...);
604 };
605 
606 template <int argc, typename Accessor, typename Parent>
607 struct ServerField : Parent
608 {
609  ServerField(Parent parent) : Parent(parent) {}
610 
611  const Parent& parent() const { return *this; }
612 
613  template <typename ServerContext, typename ArgTypes, typename... Args>
614  decltype(auto) invoke(ServerContext& server_context, ArgTypes, Args&&... args) const
615  {
616  return PassField<Accessor>(Priority<2>(),
617  typename Split<argc, ArgTypes>::First(),
618  server_context,
619  this->parent(),
621  std::forward<Args>(args)...);
622  }
623 };
624 
625 template <int argc, typename Accessor, typename Parent>
627 {
628  return {parent};
629 }
630 
631 template <typename Request>
633 
634 template <typename _Params, typename _Results>
635 struct CapRequestTraits<::capnp::Request<_Params, _Results>>
636 {
637  using Params = _Params;
638  using Results = _Results;
639 };
640 
644 template <typename Client>
645 void clientDestroy(Client& client)
646 {
647  if (client.m_context.connection) {
648  MP_LOG(*client.m_context.loop, Log::Debug) << "IPC client destroy " << typeid(client).name();
649  } else {
650  KJ_LOG(INFO, "IPC interrupted client destroy", typeid(client).name());
651  }
652 }
653 
654 template <typename Server>
655 void serverDestroy(Server& server)
656 {
657  MP_LOG(*server.m_context.loop, Log::Debug) << "IPC server destroy " << typeid(server).name();
658 }
659 
669 template <typename ProxyClient, typename GetRequest, typename... FieldObjs>
670 void clientInvoke(ProxyClient& proxy_client, const GetRequest& get_request, FieldObjs&&... fields)
671 {
672  if (!g_thread_context.waiter) {
674  g_thread_context.thread_name = ThreadName(proxy_client.m_context.loop->m_exe_name);
675  // If next assert triggers, it means clientInvoke is being called from
676  // the capnp event loop thread. This can happen when a ProxyServer
677  // method implementation that runs synchronously on the event loop
678  // thread tries to make a blocking callback to the client. Any server
679  // method that makes a blocking callback or blocks in general needs to
680  // run asynchronously off the event loop thread. This is easy to fix by
681  // just adding a 'context :Proxy.Context' argument to the capnp method
682  // declaration so the server method runs in a dedicated thread.
684  g_thread_context.waiter = std::make_unique<Waiter>();
685  MP_LOGPLAIN(*proxy_client.m_context.loop, Log::Info)
686  << "{" << g_thread_context.thread_name
687  << "} IPC client first request from current thread, constructing waiter";
688  }
689  ThreadContext& thread_context{g_thread_context};
690  std::optional<ClientInvokeContext> invoke_context; // Must outlive waiter->wait() call below
691  std::exception_ptr exception;
692  std::string kj_exception;
693  bool done = false;
694  const char* disconnected = nullptr;
695  proxy_client.m_context.loop->sync([&]() {
696  if (!proxy_client.m_context.connection) {
697  const Lock lock(thread_context.waiter->m_mutex);
698  done = true;
699  disconnected = "IPC client method called after disconnect.";
700  thread_context.waiter->m_cv.notify_all();
701  return;
702  }
703 
704  auto request = (proxy_client.m_client.*get_request)(nullptr);
705  using Request = CapRequestTraits<decltype(request)>;
707  invoke_context.emplace(*proxy_client.m_context.connection, thread_context);
708  IterateFields().handleChain(*invoke_context, request, FieldList(), typename FieldObjs::BuildParams{&fields}...);
709  MP_LOGPLAIN(*proxy_client.m_context.loop, Log::Debug)
710  << "{" << thread_context.thread_name << "} IPC client send "
711  << TypeName<typename Request::Params>();
712  MP_LOGPLAIN(*proxy_client.m_context.loop, Log::Trace)
713  << "send data: " << LogEscape(request.toString(), proxy_client.m_context.loop->m_log_opts.max_chars);
714 
715  proxy_client.m_context.loop->m_task_set->add(request.send().then(
716  [&](::capnp::Response<typename Request::Results>&& response) {
717  MP_LOGPLAIN(*proxy_client.m_context.loop, Log::Debug)
718  << "{" << thread_context.thread_name << "} IPC client recv "
719  << TypeName<typename Request::Results>();
720  MP_LOGPLAIN(*proxy_client.m_context.loop, Log::Trace)
721  << "recv data: " << LogEscape(response.toString(), proxy_client.m_context.loop->m_log_opts.max_chars);
722  try {
724  *invoke_context, response, FieldList(), typename FieldObjs::ReadResults{&fields}...);
725  } catch (...) {
726  exception = std::current_exception();
727  }
728  const Lock lock(thread_context.waiter->m_mutex);
729  done = true;
730  thread_context.waiter->m_cv.notify_all();
731  },
732  [&](const ::kj::Exception& e) {
733  if (e.getType() == ::kj::Exception::Type::DISCONNECTED) {
734  disconnected = "IPC client method call interrupted by disconnect.";
735  } else {
736  kj_exception = kj::str("kj::Exception: ", e).cStr();
737  MP_LOGPLAIN(*proxy_client.m_context.loop, Log::Info)
738  << "{" << thread_context.thread_name << "} IPC client exception " << kj_exception;
739  }
740  const Lock lock(thread_context.waiter->m_mutex);
741  done = true;
742  thread_context.waiter->m_cv.notify_all();
743  }));
744  });
745 
746  Lock lock(thread_context.waiter->m_mutex);
747  thread_context.waiter->wait(lock, [&done]() { return done; });
748  if (exception) std::rethrow_exception(exception);
749  if (!kj_exception.empty()) MP_LOGPLAIN(*proxy_client.m_context.loop, Log::Raise) << kj_exception;
750  if (disconnected) MP_LOGPLAIN(*proxy_client.m_context.loop, Log::Raise) << disconnected;
751 }
752 
756 template <typename Fn, typename Ret>
757 auto ReplaceVoid(Fn&& fn, Ret&& ret)
758 {
759  if constexpr (std::is_same_v<decltype(fn()), void>) {
760  fn();
761  return ret();
762  } else {
763  return fn();
764  }
765 }
766 
767 extern std::atomic<int> server_reqs;
768 
776 template <typename Server, typename CallContext, typename Fn>
777 kj::Promise<void> serverInvoke(Server& server, CallContext& call_context, Fn fn)
778 {
779  auto params = call_context.getParams();
780  using Params = decltype(params);
781  using Results = typename decltype(call_context.getResults())::Builds;
782 
783  int req = ++server_reqs;
784  MP_LOG(*server.m_context.loop, Log::Debug) << "IPC server recv request #" << req << " "
785  << TypeName<typename Params::Reads>();
786  MP_LOG(*server.m_context.loop, Log::Trace) << "request data: "
787  << LogEscape(params.toString(), server.m_context.loop->m_log_opts.max_chars);
788 
789  try {
792  ServerContext server_context{server, call_context, req};
793  // ReplaceVoid is used to support fn.invoke implementations that
794  // execute asynchronously and return promises, as well as
795  // implementations that execute synchronously and return void. The
796  // invoke function will be synchronous by default, but asynchronous if
797  // an mp.Context argument is passed, and the mp.Context PassField
798  // overload returns a promise executing the request in a worker thread
799  // and waiting for it to complete.
800  return ReplaceVoid([&]() { return fn.invoke(server_context, ArgList()); },
801  [&]() { return kj::Promise<CallContext>(kj::mv(call_context)); })
802  .then([&server, req](CallContext call_context) {
803  MP_LOG(*server.m_context.loop, Log::Debug) << "IPC server send response #" << req << " " << TypeName<Results>();
804  MP_LOG(*server.m_context.loop, Log::Trace) << "response data: "
805  << LogEscape(call_context.getResults().toString(), server.m_context.loop->m_log_opts.max_chars);
806  });
807  } catch (const std::exception& e) {
808  MP_LOG(*server.m_context.loop, Log::Error) << "IPC server unhandled exception: " << e.what();
809  throw;
810  } catch (...) {
811  MP_LOG(*server.m_context.loop, Log::Error) << "IPC server unhandled exception";
812  throw;
813  }
814 }
815 
819  template<typename Interface>
821  types().emplace(typeid(Interface), [](void* iface) -> ProxyContext& { return static_cast<typename mp::ProxyType<Interface>::Client&>(*static_cast<Interface*>(iface)).m_context; });
822  }
823  using Types = std::map<std::type_index, ProxyContext&(*)(void*)>;
824  static Types& types() { static Types types; return types; }
825 };
826 
827 } // namespace mp
828 
829 #endif // MP_PROXY_TYPES_H
ClientParam< Accessor, Types... > MakeClientParam(Types &&... values)
Definition: proxy-types.h:487
void handleChain(Arg1 &arg1, Arg2 &arg2, ParamList)
Definition: proxy-types.h:388
std::tuple< Types &&... > m_values
Definition: proxy-types.h:483
ValueField(Value &value)
Definition: proxy-types.h:22
int ret
assert(!tx.IsCoinBase())
void clientInvoke(ProxyClient &proxy_client, const GetRequest &get_request, FieldObjs &&... fields)
Entry point called by generated client code that looks like:
Definition: proxy-types.h:670
ReadDestEmplace(TypeList< LocalType >, EmplaceFn emplace_fn)
Definition: proxy-types.h:96
auto ReplaceVoid(Fn &&fn, Ret &&ret)
Invoke callable fn() that may return void.
Definition: proxy-types.h:757
Function parameter type for prioritizing overloaded function calls that would otherwise be ambiguous...
Definition: util.h:108
The thread_local ThreadContext g_thread_context struct provides information about individual threads ...
Definition: proxy-io.h:674
std::remove_cv_t< std::remove_reference_t< T > > RemoveCvRef
Substitutue for std::remove_cvref_t.
Definition: util.h:82
std::string ThreadName(const char *exe_name)
Format current thread name as "{exe_name}-{$pid}/{thread_name}-{$tid}".
Definition: util.cpp:64
bool CustomHasField(TypeList< LocalTypes... >, InvokeContext &invoke_context, const Input &input)
Return whether to read a C++ value from a Cap&#39;n Proto field.
Definition: proxy-types.h:207
std::atomic< int > server_reqs
Definition: proxy.cpp:434
void CustomPassField()
StructField(S &struct_)
Definition: proxy-types.h:36
#define S(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p)
auto ReadDestTemp()
Helper function to create a ReadDestEmplace object that constructs a temporary, ReadField can return...
Definition: proxy-types.h:133
Accessor type holding flags that determine how to access a message field.
Definition: proxy.h:315
void setHas() const
Definition: proxy-types.h:69
Definition: common.h:29
ListOutput(Builder &builder, size_t index)
Definition: proxy-types.h:271
bool has() const
Definition: proxy-types.h:43
void handleField(InvokeContext &invoke_context, Params &params, ParamList)
Definition: proxy-types.h:411
static const int64_t values[]
A selection of numbers that do not trigger int64_t overflow when added/subtracted.
Exception thrown from code executing an IPC call that is interrupted.
Definition: util.h:278
void invoke(ServerContext &server_context, TypeList<>, Args &&... args) const
Definition: proxy-types.h:562
char * CharCast(char *c)
Definition: util.h:272
ServerField< argc, Accessor, Parent > MakeServerField(Parent parent)
Definition: proxy-types.h:626
Generic utility functions used by capnp code.
Definition: util.h:32
Customizable (through template specialization) traits class used in generated ProxyServer implementat...
Definition: proxy.h:303
ClientParam(Types &&... values)
Definition: proxy-types.h:438
ReadResults(ClientException *client_exception)
Definition: proxy-types.h:430
void handleField(Arg1 &&, Arg2 &&, ParamList)
Definition: proxy-types.h:400
void clientDestroy(Client &client)
Entry point called by all generated ProxyClient destructors.
Definition: proxy-types.h:645
Destination parameter type that can be passed to ReadField function as an alternative to ReadDestEmpl...
Definition: proxy-types.h:145
Specialization of above (base case)
Definition: util.h:114
ProxyTypeRegister(TypeList< Interface >)
Definition: proxy-types.h:820
Value & update(UpdateFn &&update_fn)
Simple case. If ReadField works by calling update() just forward arguments to update_fn.
Definition: proxy-types.h:151
typename ::capnp::List< T, kind >::Builder Builder
Definition: proxy-types.h:269
Functions to serialize / deserialize common bitcoin types.
Definition: common-types.h:57
Context data associated with proxy client and server classes.
Definition: proxy.h:68
decltype(auto) get() const
Definition: proxy-types.h:41
ReadDestUpdate(Value &value)
Definition: proxy-types.h:147
kj::Promise< void > serverInvoke(Server &server, CallContext &call_context, Fn fn)
Entry point called by generated server code that looks like:
Definition: proxy-types.h:777
void MaybeSetWant(TypeList< LocalType *>, Priority< 1 >, const Value &value, Output &&output)
Definition: proxy-types.h:327
void serverDestroy(Server &server)
Definition: proxy-types.h:655
ServerRet(Parent parent)
Definition: proxy-types.h:543
ArgsManager & args
Definition: bitcoind.cpp:277
Value & init()
Definition: proxy-types.h:28
ServerExcept(Parent parent)
Definition: proxy-types.h:559
typename _Require< SfinaeExpr, Result >::Result Require
SFINAE helper, basically the same as to C++17&#39;s void_t, but allowing types other than void to be retu...
Definition: util.h:97
#define B
Definition: util_tests.cpp:561
const char * name
Definition: rest.cpp:48
thread_local ThreadContext g_thread_context
Definition: proxy.cpp:41
bool loop_thread
Whether this thread is a capnp event loop thread.
Definition: proxy-io.h:729
std::unique_ptr< interfaces::Init > init
LocalType BuildPrimitive(InvokeContext &invoke_context, const Value &value, TypeList< LocalType >, typename std::enable_if< std::is_enum< Value >::value >::type *enable=nullptr)
Definition: type-number.h:12
ServerInvokeContext< ProxyServer< Interface >, ::capnp::CallContext< Params, Results > > ServerContext
Definition: proxy-io.h:72
bool has() const
Definition: proxy-types.h:29
std::map< std::type_index, ProxyContext &(*)(void *)> Types
Definition: proxy-types.h:823
Type helper splitting a TypeList into two halves at position index.
Definition: util.h:57
decltype(auto) invoke(ServerContext &server_context, TypeList<>, Args &&... args) const
Definition: proxy-types.h:496
ProxyServer & proxy_server
Definition: proxy-io.h:48
Map to convert client interface pointers to ProxyContext struct references at runtime using typeids...
Definition: proxy-types.h:818
ValueField(Value &&value)
Definition: proxy-types.h:23
Mapping from local c++ type to capnp type and traits (specializations are generated by proxy-codegen...
Definition: proxy.h:34
ServerField(Parent parent)
Definition: proxy-types.h:609
EmplaceFn m_emplace_fn
Definition: proxy-types.h:127
ReadResults(ClientParam *client_param)
Definition: proxy-types.h:479
std::string thread_name
Identifying string for debug.
Definition: proxy-io.h:677
void BuildField(TypeList< LocalTypes... >, Context &context, Output &&output, Values &&... values)
Definition: proxy-types.h:250
static const bool requested
Definition: proxy.h:320
void MaybeReadField(std::true_type, Args &&... args)
Definition: proxy-types.h:317
void invoke(ServerContext &server_context, TypeList<>, Args &&... args) const
Definition: proxy-types.h:534
ClientException * m_client_exception
Definition: proxy-types.h:431
decltype(auto) TryFinally(Fn &&fn, After &&after)
Invoke a function and run a follow-up action before returning the original result.
Definition: util.h:223
decltype(auto) MaybeGet(Message &&message, decltype(Accessor::get(message)) *enable=nullptr)
Helper for CustomPassField below.
Definition: proxy-types.h:576
decltype(auto) update(UpdateFn &&update_fn)
More complicated case.
Definition: proxy-types.h:111
#define MP_LOGPLAIN(loop,...)
Definition: proxy-io.h:207
auto result
Definition: common-types.h:74
decltype(auto) CustomReadField(TypeList< LocalType >, Priority< 1 >, InvokeContext &invoke_context, Input &&input, ReadDest &&read_dest) requires Unserializable< LocalType
Overload multiprocess library&#39;s CustomReadField hook to allow any object with an Unserialize method t...
Definition: common-types.h:100
static const bool optional
Definition: proxy.h:319
void CustomBuildField(TypeList< LocalType >, Priority< 1 >, InvokeContext &invoke_context, Value &&value, Output &&output) requires Serializable< LocalType
Overload multiprocess library&#39;s CustomBuildField hook to allow any serializable object to be stored i...
Value & m_value
Definition: proxy-types.h:24
std::string LogEscape(const kj::StringTree &string, size_t max_size)
Escape binary string for use in log so it doesn&#39;t trigger unicode decode errors in python unit tests...
Definition: util.cpp:95
const CChainParams & Params()
Return the currently selected parameters.
#define MP_LOG(loop,...)
Definition: proxy-io.h:209
BuildParams(ClientParam *client_param)
Definition: proxy-types.h:462
std::decay_t< T > Decay
Type helper abbreviating std::decay.
Definition: util.h:86
decltype(auto) ReadField(TypeList< LocalTypes... >, InvokeContext &invoke_context, Input &&input, Args &&... args)
Definition: proxy-types.h:213
decltype(auto) init(Arg &&arg) const
Definition: proxy-types.h:279
void ThrowField(TypeList< LocalType >, InvokeContext &invoke_context, Input &&input)
Definition: proxy-types.h:219
void handleField(ClientInvokeContext &invoke_context, Params &params, ParamList)
Definition: proxy-types.h:443
ClientException * m_client_exception
Definition: proxy-types.h:416
BuildParams(ClientException *client_exception)
Definition: proxy-types.h:415
decltype(auto) set(Args &&...args) const
Definition: proxy-types.h:61
void setWant() const
Definition: proxy-types.h:75
bool want() const
Definition: proxy-types.h:53
static const bool boxed
Definition: proxy.h:321
Definition: util.h:171
static const bool in
Definition: proxy.h:317
std::unique_ptr< Waiter > waiter
Waiter object used to allow remote clients to execute code on this thread.
Definition: proxy-io.h:694
void invoke(ServerContext &server_context, TypeList<>, Args &&... args) const
Definition: proxy-types.h:546
Value & construct(Args &&... args)
More complicated case.
Definition: proxy-types.h:160
CallContext & call_context
Definition: proxy-io.h:49
auto PassField(Priority< 1 >, TypeList< LocalType &>, ServerContext &server_context, Fn &&fn, Args &&... args) -> Require< typename decltype(Accessor::get(server_context.call_context.getParams()))::Calls >
PassField override for callable interface reference arguments.
Definition: proxy-types.h:291
bool CustomHasValue(InvokeContext &invoke_context, const Values &... value)
Return whether to write a C++ value into a Cap&#39;n Proto field.
Definition: proxy-types.h:244
void handleField(ClientInvokeContext &invoke_context, Results &results, TypeList< Params... >)
Definition: proxy-types.h:469
Struct & m_struct
Definition: proxy-types.h:39
decltype(auto) invoke(ServerContext &server_context, ArgTypes, Args &&... args) const
Definition: proxy-types.h:614
void handleField(InvokeContext &invoke_context, Results &results, ParamList)
Definition: proxy-types.h:422
void handleChain(Arg1 &arg1, Arg2 &arg2, ParamList, NextFn &&next_fn, NextFnArgs &&... next_fn_args)
Definition: proxy-types.h:379
Mapping from capnp interface type to proxy client implementation (specializations are generated by pr...
Definition: proxy.h:25
const Parent & parent() const
Definition: proxy-types.h:611
void MaybeBuildField(std::true_type, Args &&... args)
Definition: proxy-types.h:308
static Types & types()
Definition: proxy-types.h:824
decltype(auto) construct(Args &&... args)
Simple case.
Definition: proxy-types.h:101