Bitcoin Core 31.99.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
16namespace mp {
17
18template <typename Value>
20{
21public:
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
32template <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.
93template <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
132template <typename LocalType>
134{
135 return ReadDestEmplace{TypeList<LocalType>(), [](auto&&... args) -> decltype(auto) {
136 return LocalType{std::forward<decltype(args)>(args)...};
137 }};
138}
139
144template <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
206template <typename... LocalTypes, typename Input>
207bool CustomHasField(TypeList<LocalTypes...>, InvokeContext& invoke_context, const Input& input)
208{
209 return input.has();
210}
211
212template <typename... LocalTypes, typename Input, typename... Args>
213decltype(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
218template <typename LocalType, typename Input>
219void 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
229template <typename Input>
230void 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
243template <typename... Values>
244bool CustomHasValue(InvokeContext& invoke_context, const Values&... value)
245{
246 return true;
247}
248
249template <typename... LocalTypes, typename Context, typename... Values, typename Output>
250void 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.
263template <typename ListType>
265
266template <typename T, ::capnp::Kind kind>
267struct 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
283template <typename LocalType, typename Value, typename Output>
284void BuildList(TypeList<LocalType>, InvokeContext& invoke_context, Output&& output, Value&& value)
285{
286 auto list = output.init(value.size());
287 size_t i = 0;
288 for (const auto& elem : value) {
289 BuildField(TypeList<LocalType>(), invoke_context, ListOutput<typename decltype(list)::Builds>(list, i), elem);
290 ++i;
291 }
292}
293
294template <typename LocalType, typename Input, typename ReadDest, typename InitFn, typename EmplaceFn>
295decltype(auto) ReadList(TypeList<LocalType>, InvokeContext& invoke_context, Input&& input, ReadDest&& read_dest, InitFn&& init, EmplaceFn&& emplace)
296{
297 return read_dest.update([&](auto& value) {
298 auto data = input.get();
299 init(value, data.size());
300 for (auto item : data) {
301 ReadField(TypeList<LocalType>(), invoke_context, Make<ValueField>(item),
302 ReadDestEmplace(TypeList<LocalType>(), [&emplace, &value](auto&&... args) -> decltype(auto) {
303 return emplace(value, std::forward<decltype(args)>(args)...);
304 }));
305 }
306 });
307}
308
309template <typename LocalType, typename Value, typename Output>
310void CustomBuildField(TypeList<LocalType>, Priority<0>, InvokeContext& invoke_context, Value&& value, Output&& output)
311{
312 output.set(BuildPrimitive(invoke_context, std::forward<Value>(value), TypeList<decltype(output.get())>()));
313}
314
316template <typename Accessor, typename LocalType, typename ServerContext, typename Fn, typename... Args>
317auto PassField(Priority<1>, TypeList<LocalType&>, ServerContext& server_context, Fn&& fn, Args&&... args)
318 -> Require<typename decltype(Accessor::get(server_context.call_context.getParams()))::Calls>
319{
320 // Just create a temporary ProxyClient if argument is a reference to an
321 // interface client. If argument needs to have a longer lifetime and not be
322 // destroyed after this call, a CustomPassField overload can be implemented
323 // to bypass this code, and a custom ProxyServerMethodTraits overload can be
324 // implemented in order to read the capability pointer out of params and
325 // construct a ProxyClient with a longer lifetime.
326 const auto& params = server_context.call_context.getParams();
327 const auto& input = Make<StructField, Accessor>(params);
328 using Interface = typename Decay<decltype(input.get())>::Calls;
329 auto param = std::make_unique<ProxyClient<Interface>>(input.get(), server_context.proxy_server.m_context.connection, false);
330 fn.invoke(server_context, std::forward<Args>(args)..., *param);
331}
332
333template <typename... Args>
334void MaybeBuildField(std::true_type, Args&&... args)
335{
336 BuildField(std::forward<Args>(args)...);
337}
338template <typename... Args>
339void MaybeBuildField(std::false_type, Args&&...)
340{
341}
342template <typename... Args>
343void MaybeReadField(std::true_type, Args&&... args)
344{
345 ReadField(std::forward<Args>(args)...);
346}
347template <typename... Args>
348void MaybeReadField(std::false_type, Args&&...)
349{
350}
351
352template <typename LocalType, typename Value, typename Output>
353void MaybeSetWant(TypeList<LocalType*>, Priority<1>, const Value& value, Output&& output)
354{
355 if (value) {
356 output.setWant();
357 }
358}
359
360template <typename LocalTypes, typename... Args>
361void MaybeSetWant(LocalTypes, Priority<0>, const Args&...)
362{
363}
364
366template <typename Accessor, typename LocalType, typename ServerContext, typename Fn, typename... Args>
367void PassField(Priority<0>, TypeList<LocalType>, ServerContext& server_context, Fn&& fn, Args&&... args)
368{
369 InvokeContext& invoke_context = server_context;
370 using ArgType = RemoveCvRef<LocalType>;
371 std::optional<ArgType> param;
372 const auto& params = server_context.call_context.getParams();
373 MaybeReadField(std::integral_constant<bool, Accessor::in>(), TypeList<ArgType>(), invoke_context,
374 Make<StructField, Accessor>(params), ReadDestEmplace(TypeList<ArgType>(), [&](auto&&... args) -> auto& {
375 param.emplace(std::forward<decltype(args)>(args)...);
376 return *param;
377 }));
378 if constexpr (Accessor::in) {
379 assert(param);
380 } else {
381 if (!param) param.emplace();
382 }
383 fn.invoke(server_context, std::forward<Args>(args)..., static_cast<LocalType&&>(*param));
384 auto&& results = server_context.call_context.getResults();
385 MaybeBuildField(std::integral_constant<bool, Accessor::out>(), TypeList<LocalType>(), invoke_context,
386 Make<StructField, Accessor>(results), *param);
387}
388
390template <typename Accessor, typename ServerContext, typename Fn, typename... Args>
391void PassField(Priority<0>, TypeList<>, ServerContext& server_context, const Fn& fn, Args&&... args)
392{
393 const auto& params = server_context.call_context.getParams();
394 const auto& input = Make<StructField, Accessor>(params);
395 ReadField(TypeList<>(), server_context, input);
396 fn.invoke(server_context, std::forward<Args>(args)...);
397 auto&& results = server_context.call_context.getResults();
398 BuildField(TypeList<>(), server_context, Make<StructField, Accessor>(results));
399}
400
401template <typename Derived, size_t N = 0>
403{
404 template <typename Arg1, typename Arg2, typename ParamList, typename NextFn, typename... NextFnArgs>
405 void handleChain(Arg1& arg1, Arg2& arg2, ParamList, NextFn&& next_fn, NextFnArgs&&... next_fn_args)
406 {
407 using S = Split<N, ParamList>;
408 handleChain(arg1, arg2, typename S::First());
409 next_fn.handleChain(arg1, arg2, typename S::Second(),
410 std::forward<NextFnArgs>(next_fn_args)...);
411 }
412
413 template <typename Arg1, typename Arg2, typename ParamList>
414 void handleChain(Arg1& arg1, Arg2& arg2, ParamList)
415 {
416 static_cast<Derived*>(this)->handleField(arg1, arg2, ParamList());
417 }
418private:
420 friend Derived;
421};
422
423struct IterateFields : IterateFieldsHelper<IterateFields, 0>
424{
425 template <typename Arg1, typename Arg2, typename ParamList>
426 void handleField(Arg1&&, Arg2&&, ParamList)
427 {
428 }
429};
430
431template <typename Exception, typename Accessor>
433{
434 struct BuildParams : IterateFieldsHelper<BuildParams, 0>
435 {
436 template <typename Params, typename ParamList>
437 void handleField(InvokeContext& invoke_context, Params& params, ParamList)
438 {
439 }
440
441 BuildParams(ClientException* client_exception) : m_client_exception(client_exception) {}
443 };
444
445 struct ReadResults : IterateFieldsHelper<ReadResults, 0>
446 {
447 template <typename Results, typename ParamList>
448 void handleField(InvokeContext& invoke_context, Results& results, ParamList)
449 {
450 StructField<Accessor, Results> input(results);
451 if (CustomHasField(TypeList<Exception>(), invoke_context, input)) {
452 ThrowField(TypeList<Exception>(), invoke_context, input);
453 }
454 }
455
456 ReadResults(ClientException* client_exception) : m_client_exception(client_exception) {}
458 };
459};
460
461template <typename Accessor, typename... Types>
463{
464 ClientParam(Types&&... values) : m_values{std::forward<Types>(values)...} {}
465
466 struct BuildParams : IterateFieldsHelper<BuildParams, sizeof...(Types)>
467 {
468 template <typename Params, typename ParamList>
469 void handleField(ClientInvokeContext& invoke_context, Params& params, ParamList)
470 {
471 auto const fun = [&]<typename... Values>(Values&&... values) {
473 ParamList(), Priority<1>(), values..., Make<StructField, Accessor>(params));
474 MaybeBuildField(std::integral_constant<bool, Accessor::in>(), ParamList(), invoke_context,
475 Make<StructField, Accessor>(params), std::forward<Values>(values)...);
476 };
477
478 // Note: The m_values tuple just consists of lvalue and rvalue
479 // references, so calling std::move doesn't change the tuple, it
480 // just causes std::apply to call the std::get overload that returns
481 // && instead of &, so rvalue references are preserved and not
482 // turned into lvalue references. This allows the BuildField call to
483 // move from the argument if it is an rvalue reference or was passed
484 // by value.
485 std::apply(fun, std::move(m_client_param->m_values));
486 }
487
488 BuildParams(ClientParam* client_param) : m_client_param(client_param) {}
490 };
491
492 struct ReadResults : IterateFieldsHelper<ReadResults, sizeof...(Types)>
493 {
494 template <typename Results, typename... Params>
495 void handleField(ClientInvokeContext& invoke_context, Results& results, TypeList<Params...>)
496 {
497 auto const fun = [&]<typename... Values>(Values&&... values) {
498 MaybeReadField(std::integral_constant<bool, Accessor::out>(), TypeList<Decay<Params>...>(), invoke_context,
499 Make<StructField, Accessor>(results), ReadDestUpdate(values)...);
500 };
501
502 std::apply(fun, m_client_param->m_values);
503 }
504
505 ReadResults(ClientParam* client_param) : m_client_param(client_param) {}
507 };
508
509 std::tuple<Types&&...> m_values;
510};
511
512template <typename Accessor, typename... Types>
514{
515 return {std::forward<Types>(values)...};
516}
517
519{
520 // FIXME: maybe call call_context.releaseParams()
521 template <typename ServerContext, typename... Args>
522 decltype(auto) invoke(ServerContext& server_context, TypeList<>, Args&&... args) const
523 {
524 // If cancel_lock is set, release it while executing the method, and
525 // reacquire it afterwards. The lock is needed to prevent params and
526 // response structs from being deleted by the event loop thread if the
527 // request is canceled, so it is only needed before and after method
528 // execution. It is important to release the lock during execution
529 // because the method can take arbitrarily long to return and the event
530 // loop will need the lock itself in on_cancel if the call is canceled.
531 if (server_context.cancel_lock) server_context.cancel_lock->m_lock.unlock();
532 return TryFinally(
533 [&]() -> decltype(auto) {
535 typename decltype(server_context.call_context.getParams())::Reads
536 >::invoke(server_context, std::forward<Args>(args)...);
537 },
538 [&] {
539 if (server_context.cancel_lock) server_context.cancel_lock->m_lock.lock();
540 // If the IPC request was canceled, throw InterruptException
541 // because there is no point continuing and trying to fill the
542 // call_context.getResults() struct. It's also important to stop
543 // executing because the connection may have been destroyed as
544 // described in https://github.com/bitcoin/bitcoin/issues/34250
545 // and there could be invalid references to the destroyed
546 // Connection object if this continued.
547 // If the IPC method itself threw an exception, the
548 // InterruptException thrown below will take precedence over it.
549 // Since the call has been canceled that exception can't be
550 // returned to the caller, so it needs to be discarded like
551 // other result values.
552 if (server_context.request_canceled) throw InterruptException{"canceled"};
553 });
554 }
555};
556
558{
559 template <typename ServerContext, typename... Args>
560 void invoke(ServerContext& server_context, TypeList<>, Args&&... args) const
561 {
562 server_context.proxy_server.invokeDestroy(std::forward<Args>(args)...);
563 }
564};
565
566template <typename Accessor, typename Parent>
567struct ServerRet : Parent
568{
569 ServerRet(Parent parent) : Parent(parent) {}
570
571 template <typename ServerContext, typename... Args>
572 void invoke(ServerContext& server_context, TypeList<>, Args&&... args) const
573 {
574 auto&& result = Parent::invoke(server_context, TypeList<>(), std::forward<Args>(args)...);
575 auto&& results = server_context.call_context.getResults();
576 InvokeContext& invoke_context = server_context;
577 BuildField(TypeList<decltype(result)>(), invoke_context, Make<StructField, Accessor>(results),
578 std::forward<decltype(result)>(result));
579 }
580};
581
582template <typename Exception, typename Accessor, typename Parent>
583struct ServerExcept : Parent
584{
585 ServerExcept(Parent parent) : Parent(parent) {}
586
587 template <typename ServerContext, typename... Args>
588 void invoke(ServerContext& server_context, TypeList<>, Args&&... args) const
589 {
590 try {
591 return Parent::invoke(server_context, TypeList<>(), std::forward<Args>(args)...);
592 } catch (const Exception& exception) {
593 auto&& results = server_context.call_context.getResults();
594 BuildField(TypeList<Exception>(), server_context, Make<StructField, Accessor>(results), exception);
595 }
596 }
597};
598
601template <typename Accessor, typename Message>
602decltype(auto) MaybeGet(Message&& message, decltype(Accessor::get(message))* enable = nullptr)
603{
604 return Accessor::get(message);
605}
606
607template <typename Accessor>
608::capnp::Void MaybeGet(...)
609{
610 return {};
611}
612
613template <class Accessor>
615
626template <typename Accessor, typename... Args>
627auto PassField(Priority<2>, Args&&... args) -> decltype(CustomPassField<Accessor>(std::forward<Args>(args)...))
628{
629 return CustomPassField<Accessor>(std::forward<Args>(args)...);
630};
631
632template <int argc, typename Accessor, typename Parent>
633struct ServerField : Parent
634{
635 ServerField(Parent parent) : Parent(parent) {}
636
637 const Parent& parent() const { return *this; }
638
639 template <typename ServerContext, typename ArgTypes, typename... Args>
640 decltype(auto) invoke(ServerContext& server_context, ArgTypes, Args&&... args) const
641 {
642 return PassField<Accessor>(Priority<2>(),
644 server_context,
645 this->parent(),
647 std::forward<Args>(args)...);
648 }
649};
650
651template <int argc, typename Accessor, typename Parent>
653{
654 return {parent};
655}
656
657template <typename Request>
659
660template <typename _Params, typename _Results>
661struct CapRequestTraits<::capnp::Request<_Params, _Results>>
662{
663 using Params = _Params;
664 using Results = _Results;
665};
666
670template <typename Client>
672{
673 MP_LOG(*client.m_context.loop, Log::Debug) << "IPC client destroy " << CxxTypeName(client);
674}
675
676template <typename Server>
677void serverDestroy(Server& server)
678{
679 MP_LOG(*server.m_context.loop, Log::Debug) << "IPC server destroy " << CxxTypeName(server);
680}
681
691template <typename ProxyClient, typename GetRequest, typename... FieldObjs>
692void clientInvoke(ProxyClient& proxy_client, const GetRequest& get_request, FieldObjs&&... fields)
693{
696 g_thread_context.thread_name = ThreadName(proxy_client.m_context.loop->m_exe_name);
697 // If next assert triggers, it means clientInvoke is being called from
698 // the capnp event loop thread. This can happen when a ProxyServer
699 // method implementation that runs synchronously on the event loop
700 // thread tries to make a blocking callback to the client. Any server
701 // method that makes a blocking callback or blocks in general needs to
702 // run asynchronously off the event loop thread. This is easy to fix by
703 // just adding a 'context :Proxy.Context' argument to the capnp method
704 // declaration so the server method runs in a dedicated thread.
706 g_thread_context.waiter = std::make_unique<Waiter>();
707 MP_LOGPLAIN(*proxy_client.m_context.loop, Log::Info)
709 << "} IPC client first request from current thread, constructing waiter";
710 }
711 ThreadContext& thread_context{g_thread_context};
712 std::optional<ClientInvokeContext> invoke_context; // Must outlive waiter->wait() call below
713 std::exception_ptr exception;
714 std::string kj_exception;
715 bool done = false;
716 const char* disconnected = nullptr;
717 proxy_client.m_context.loop->sync([&]() {
718 if (!proxy_client.m_context.connection) {
719 const Lock lock(thread_context.waiter->m_mutex);
720 done = true;
721 disconnected = "IPC client method called after disconnect.";
722 thread_context.waiter->m_cv.notify_all();
723 return;
724 }
725
726 auto request = (proxy_client.m_client.*get_request)(nullptr);
727 using Request = CapRequestTraits<decltype(request)>;
729 invoke_context.emplace(*proxy_client.m_context.connection, thread_context);
730 IterateFields().handleChain(*invoke_context, request, FieldList(), typename FieldObjs::BuildParams{&fields}...);
731 MP_LOGPLAIN(*proxy_client.m_context.loop, Log::Debug)
732 << "{" << thread_context.thread_name << "} IPC client send "
733 << TypeName<typename Request::Params>();
734 MP_LOGPLAIN(*proxy_client.m_context.loop, Log::Trace)
735 << "send data: " << LogEscape(request.toString(), proxy_client.m_context.loop->m_log_opts.max_chars);
736
737 proxy_client.m_context.loop->m_task_set->add(request.send().then(
738 [&](::capnp::Response<typename Request::Results>&& response) {
739 MP_LOGPLAIN(*proxy_client.m_context.loop, Log::Debug)
740 << "{" << thread_context.thread_name << "} IPC client recv "
741 << TypeName<typename Request::Results>();
742 MP_LOGPLAIN(*proxy_client.m_context.loop, Log::Trace)
743 << "recv data: " << LogEscape(response.toString(), proxy_client.m_context.loop->m_log_opts.max_chars);
744 try {
745 IterateFields().handleChain(
746 *invoke_context, response, FieldList(), typename FieldObjs::ReadResults{&fields}...);
747 } catch (...) {
748 exception = std::current_exception();
749 }
750 const Lock lock(thread_context.waiter->m_mutex);
751 done = true;
752 thread_context.waiter->m_cv.notify_all();
753 },
754 [&](const ::kj::Exception& e) {
755 if (e.getType() == ::kj::Exception::Type::DISCONNECTED) {
756 disconnected = "IPC client method call interrupted by disconnect.";
757 } else {
758 kj_exception = kj::str("kj::Exception: ", e).cStr();
759 MP_LOGPLAIN(*proxy_client.m_context.loop, Log::Info)
760 << "{" << thread_context.thread_name << "} IPC client exception " << kj_exception;
761 }
762 const Lock lock(thread_context.waiter->m_mutex);
763 done = true;
764 thread_context.waiter->m_cv.notify_all();
765 }));
766 });
767
768 Lock lock(thread_context.waiter->m_mutex);
769 thread_context.waiter->wait(lock, [&done]() { return done; });
770 if (exception) std::rethrow_exception(exception);
771 if (!kj_exception.empty()) MP_LOGPLAIN(*proxy_client.m_context.loop, Log::Raise) << kj_exception;
772 if (disconnected) MP_LOGPLAIN(*proxy_client.m_context.loop, Log::Raise) << disconnected;
773}
774
778template <typename Fn, typename Ret>
779auto ReplaceVoid(Fn&& fn, Ret&& ret)
780{
781 if constexpr (std::is_same_v<decltype(fn()), void>) {
782 fn();
783 return ret();
784 } else {
785 return fn();
786 }
787}
788
789extern std::atomic<int> server_reqs;
790
798template <typename Server, typename CallContext, typename Fn>
799kj::Promise<void> serverInvoke(Server& server, CallContext& call_context, Fn fn)
800{
801 auto params = call_context.getParams();
802 using Params = decltype(params);
803 using Results = typename decltype(call_context.getResults())::Builds;
804
805 EventLoop& loop = *server.m_context.loop;
806 int req = ++server_reqs;
807 MP_LOG(loop, Log::Debug) << "IPC server recv request #" << req << " "
808 << TypeName<typename Params::Reads>();
809 MP_LOG(loop, Log::Trace) << "request data: "
810 << LogEscape(params.toString(), server.m_context.loop->m_log_opts.max_chars);
811
812 try {
815 ServerContext server_context{server, call_context, req};
816 // ReplaceVoid is used to support fn.invoke implementations that
817 // execute asynchronously and return promises, as well as
818 // implementations that execute synchronously and return void. The
819 // invoke function will be synchronous by default, but asynchronous if
820 // an mp.Context argument is passed, and the mp.Context PassField
821 // overload returns a promise executing the request in a worker thread
822 // and waiting for it to complete.
823 return ReplaceVoid([&]() { return fn.invoke(server_context, ArgList()); },
824 [&]() { return kj::Promise<CallContext>(kj::mv(call_context)); })
825 .then([&loop, req](CallContext call_context) {
826 MP_LOG(loop, Log::Debug) << "IPC server send response #" << req << " " << TypeName<Results>();
827 MP_LOG(loop, Log::Trace) << "response data: "
828 << LogEscape(call_context.getResults().toString(), loop.m_log_opts.max_chars);
829 }).catch_([&loop, req](::kj::Exception&& e) -> kj::Promise<void> {
830 // Call failed for some reason. Cap'n Proto will try to send
831 // this error to the client as well, but it is good to log the
832 // failure early here and include the request number.
833 MP_LOG(loop, Log::Error) << "IPC server error request #" << req << " " << TypeName<Results>()
834 << " " << kj::str("kj::Exception: ", e.getDescription()).cStr();
835 return kj::mv(e);
836 });
837 } catch (const std::exception& e) {
838 MP_LOG(loop, Log::Error) << "IPC server unhandled exception: " << e.what();
839 throw;
840 } catch (...) {
841 MP_LOG(loop, Log::Error) << "IPC server unhandled exception";
842 throw;
843 }
844}
845
849 template<typename Interface>
851 types().emplace(typeid(Interface), [](void* iface) -> ProxyContext& { return static_cast<typename mp::ProxyType<Interface>::Client&>(*static_cast<Interface*>(iface)).m_context; });
852 }
853 using Types = std::map<std::type_index, ProxyContext&(*)(void*)>;
854 static Types& types() { static Types types; return types; }
855};
856
857} // namespace mp
858
859#endif // MP_PROXY_TYPES_H
int ret
catch(const std::exception &e)
std::unique_ptr< interfaces::Init > init
ArgsManager & args
Definition: bitcoind.cpp:280
const CChainParams & Params()
Return the currently selected parameters.
Event loop implementation.
Definition: proxy-io.h:242
LogOptions m_log_opts
Logging options.
Definition: proxy-io.h:346
void * m_context
External context pointer.
Definition: proxy-io.h:349
Definition: util.h:177
std::unique_lock< std::mutex > m_lock
Definition: util.h:189
Value & get()
Definition: proxy-types.h:27
Value & init()
Definition: proxy-types.h:28
const Value & get() const
Definition: proxy-types.h:26
ValueField(Value &&value)
Definition: proxy-types.h:23
ValueField(Value &value)
Definition: proxy-types.h:22
Value & m_value
Definition: proxy-types.h:24
bool has() const
Definition: proxy-types.h:29
static const PrecomputedData data
Precomputed COutPoint and CCoins values.
std::unique_ptr< ProxyClient< messages::FooInterface > > client
Definition: basic.cpp:8
Functions to serialize / deserialize common bitcoin types.
Definition: common-types.h:57
void MaybeBuildField(std::true_type, Args &&... args)
Definition: proxy-types.h:334
void clientDestroy(Client &client)
Entry point called by all generated ProxyClient destructors.
Definition: proxy-types.h:671
std::string CxxTypeName(const T &)
Definition: util.h:296
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:692
void BuildList(TypeList< LocalType >, InvokeContext &invoke_context, Output &&output, Value &&value)
Definition: proxy-types.h:284
void CustomPassField()
ClientParam< Accessor, Types... > MakeClientParam(Types &&... values)
Definition: proxy-types.h:513
void MaybeReadField(std::true_type, Args &&... args)
Definition: proxy-types.h:343
decltype(auto) TryFinally(Fn &&fn, After &&after)
Invoke a function and run a follow-up action before returning the original result.
Definition: util.h:229
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:799
void BuildField(TypeList< LocalTypes... >, Context &context, Output &&output, Values &&... values)
Definition: proxy-types.h:250
bool CustomHasValue(InvokeContext &invoke_context, const Values &... value)
Return whether to write a C++ value into a Cap'n Proto field.
Definition: proxy-types.h:244
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:317
std::string ThreadName(const char *exe_name)
Format current thread name as "{exe_name}-{$pid}/{thread_name}-{$tid}".
Definition: util.cpp:64
decltype(auto) CustomReadField(TypeList< LocalType >, Priority< 1 >, InvokeContext &invoke_context, Input &&input, ReadDest &&read_dest)
Overload multiprocess library's CustomReadField hook to allow any object with an Unserialize method t...
Definition: common-types.h:83
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
typename _Require< SfinaeExpr, Result >::Result Require
SFINAE helper, basically the same as to C++17's void_t, but allowing types other than void to be retu...
Definition: util.h:103
std::atomic< int > server_reqs
Definition: proxy.cpp:465
void ThrowField(TypeList< LocalType >, InvokeContext &invoke_context, Input &&input)
Definition: proxy-types.h:219
ServerInvokeContext< ProxyServer< Interface >, ::capnp::CallContext< Params, Results > > ServerContext
Definition: proxy-io.h:75
void MaybeSetWant(TypeList< LocalType * >, Priority< 1 >, const Value &value, Output &&output)
Definition: proxy-types.h:353
void serverDestroy(Server &server)
Definition: proxy-types.h:677
thread_local ThreadContext g_thread_context
Definition: proxy.cpp:44
std::decay_t< T > Decay
Type helper abbreviating std::decay.
Definition: util.h:92
auto ReadDestTemp()
Helper function to create a ReadDestEmplace object that constructs a temporary, ReadField can return.
Definition: proxy-types.h:133
std::remove_cv_t< std::remove_reference_t< T > > RemoveCvRef
Substitutue for std::remove_cvref_t.
Definition: util.h:88
decltype(auto) ReadField(TypeList< LocalTypes... >, InvokeContext &invoke_context, Input &&input, Args &&... args)
Definition: proxy-types.h:213
char * CharCast(char *c)
Definition: util.h:278
decltype(auto) MaybeGet(Message &&message, decltype(Accessor::get(message)) *enable=nullptr)
Helper for CustomPassField below.
Definition: proxy-types.h:602
auto ReplaceVoid(Fn &&fn, Ret &&ret)
Invoke callable fn() that may return void.
Definition: proxy-types.h:779
decltype(auto) ReadList(TypeList< LocalType >, InvokeContext &invoke_context, Input &&input, ReadDest &&read_dest, InitFn &&init, EmplaceFn &&emplace)
Definition: proxy-types.h:295
void CustomBuildField(TypeList< LocalType >, Priority< 1 >, InvokeContext &invoke_context, Value &&value, Output &&output)
Overload multiprocess library's CustomBuildField hook to allow any serializable object to be stored i...
Definition: common-types.h:63
ServerField< argc, Accessor, Parent > MakeServerField(Parent parent)
Definition: proxy-types.h:652
std::string LogEscape(const kj::StringTree &string, size_t max_size)
Escape binary string for use in log so it doesn't trigger unicode decode errors in python unit tests.
Definition: util.cpp:95
bool CustomHasField(TypeList< CTransaction >, InvokeContext &invoke_context, const Input &input)
Interpret empty Data fields as null CTransactionRef values.
Definition: common-types.h:138
#define S(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p)
#define MP_LOG(loop,...)
Definition: proxy-io.h:212
#define MP_LOGPLAIN(loop,...)
Definition: proxy-io.h:210
static const int64_t values[]
A selection of numbers that do not trigger int64_t overflow when added/subtracted.
Accessor type holding flags that determine how to access a message field.
Definition: proxy.h:316
static const bool boxed
Field is a Cap'n Proto pointer type (struct, list, text, data, interface) as opposed to a primitive t...
Definition: proxy.h:332
static const bool optional
Field has a companion has{Name} boolean field in the Cap'n Proto struct.
Definition: proxy.h:325
static const bool requested
Results field has a companion want{Name} boolean field in the Params struct.
Definition: proxy.h:329
static const bool in
Field is present from the Cap'n Proto Params struct (client -> server).
Definition: proxy.h:318
void handleField(InvokeContext &invoke_context, Params &params, ParamList)
Definition: proxy-types.h:437
ClientException * m_client_exception
Definition: proxy-types.h:442
BuildParams(ClientException *client_exception)
Definition: proxy-types.h:441
void handleField(InvokeContext &invoke_context, Results &results, ParamList)
Definition: proxy-types.h:448
ReadResults(ClientException *client_exception)
Definition: proxy-types.h:456
ClientException * m_client_exception
Definition: proxy-types.h:457
void handleField(ClientInvokeContext &invoke_context, Params &params, ParamList)
Definition: proxy-types.h:469
BuildParams(ClientParam *client_param)
Definition: proxy-types.h:488
ReadResults(ClientParam *client_param)
Definition: proxy-types.h:505
void handleField(ClientInvokeContext &invoke_context, Results &results, TypeList< Params... >)
Definition: proxy-types.h:495
ClientParam(Types &&... values)
Definition: proxy-types.h:464
std::tuple< Types &&... > m_values
Definition: proxy-types.h:509
Exception thrown from code executing an IPC call that is interrupted.
Definition: util.h:306
void handleChain(Arg1 &arg1, Arg2 &arg2, ParamList)
Definition: proxy-types.h:414
void handleChain(Arg1 &arg1, Arg2 &arg2, ParamList, NextFn &&next_fn, NextFnArgs &&... next_fn_args)
Definition: proxy-types.h:405
void handleField(Arg1 &&, Arg2 &&, ParamList)
Definition: proxy-types.h:426
decltype(auto) set(Arg &&arg) const
Definition: proxy-types.h:278
decltype(auto) init(Arg &&arg) const
Definition: proxy-types.h:279
typename ::capnp::List< T, kind >::Builder Builder
Definition: proxy-types.h:269
ListOutput(Builder &builder, size_t index)
Definition: proxy-types.h:271
size_t max_chars
Maximum number of characters to use when representing request and response structs as strings.
Definition: proxy-io.h:159
Specialization of above (base case)
Definition: util.h:121
Function parameter type for prioritizing overloaded function calls that would otherwise be ambiguous.
Definition: util.h:115
Mapping from capnp interface type to proxy client implementation (specializations are generated by pr...
Definition: proxy.h:25
Context data associated with proxy client and server classes.
Definition: proxy.h:69
Customizable (through template specialization) traits class used in generated ProxyServer implementat...
Definition: proxy.h:304
Mapping from local c++ type to capnp type and traits (specializations are generated by proxy-codegen....
Definition: proxy.h:34
Map to convert client interface pointers to ProxyContext struct references at runtime using typeids.
Definition: proxy-types.h:848
static Types & types()
Definition: proxy-types.h:854
ProxyTypeRegister(TypeList< Interface >)
Definition: proxy-types.h:850
std::map< std::type_index, ProxyContext &(*)(void *)> Types
Definition: proxy-types.h:853
decltype(auto) construct(Args &&... args)
Simple case.
Definition: proxy-types.h:101
ReadDestEmplace(TypeList< LocalType >, EmplaceFn emplace_fn)
Definition: proxy-types.h:96
decltype(auto) update(UpdateFn &&update_fn)
More complicated case.
Definition: proxy-types.h:111
EmplaceFn m_emplace_fn
Definition: proxy-types.h:127
Destination parameter type that can be passed to ReadField function as an alternative to ReadDestEmpl...
Definition: proxy-types.h:146
Value & update(UpdateFn &&update_fn)
Simple case. If ReadField works by calling update() just forward arguments to update_fn.
Definition: proxy-types.h:151
Value & construct(Args &&... args)
More complicated case.
Definition: proxy-types.h:160
ReadDestUpdate(Value &value)
Definition: proxy-types.h:147
decltype(auto) invoke(ServerContext &server_context, TypeList<>, Args &&... args) const
Definition: proxy-types.h:522
void invoke(ServerContext &server_context, TypeList<>, Args &&... args) const
Definition: proxy-types.h:560
ServerExcept(Parent parent)
Definition: proxy-types.h:585
void invoke(ServerContext &server_context, TypeList<>, Args &&... args) const
Definition: proxy-types.h:588
ServerField(Parent parent)
Definition: proxy-types.h:635
decltype(auto) invoke(ServerContext &server_context, ArgTypes, Args &&... args) const
Definition: proxy-types.h:640
const Parent & parent() const
Definition: proxy-types.h:637
CallContext & call_context
Definition: proxy-io.h:52
ProxyServer & proxy_server
Definition: proxy-io.h:51
bool request_canceled
For IPC methods that execute asynchronously, not on the event-loop thread, this is set to true if the...
Definition: proxy-io.h:66
Lock * cancel_lock
For IPC methods that execute asynchronously, not on the event-loop thread: lock preventing the event-...
Definition: proxy-io.h:59
void invoke(ServerContext &server_context, TypeList<>, Args &&... args) const
Definition: proxy-types.h:572
ServerRet(Parent parent)
Definition: proxy-types.h:569
Type helper splitting a TypeList into two halves at position index.
Definition: util.h:63
StructField(S &struct_)
Definition: proxy-types.h:36
void setWant() const
Definition: proxy-types.h:75
decltype(auto) set(Args &&...args) const
Definition: proxy-types.h:61
decltype(auto) init(Args &&...args) const
Definition: proxy-types.h:65
bool want() const
Definition: proxy-types.h:53
Struct & m_struct
Definition: proxy-types.h:39
decltype(auto) get() const
Definition: proxy-types.h:41
bool has() const
Definition: proxy-types.h:43
void setHas() const
Definition: proxy-types.h:69
The thread_local ThreadContext g_thread_context struct provides information about individual threads ...
Definition: proxy-io.h:706
std::unique_ptr< Waiter > waiter
Waiter object used to allow remote clients to execute code on this thread.
Definition: proxy-io.h:725
bool loop_thread
Whether this thread is a capnp event loop thread.
Definition: proxy-io.h:760
std::string thread_name
Identifying string for debug.
Definition: proxy-io.h:708
Generic utility functions used by capnp code.
Definition: util.h:39
#define B
Definition: util_tests.cpp:563
assert(!tx.IsCoinBase())