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 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
290template <typename Accessor, typename LocalType, typename ServerContext, typename Fn, typename... Args>
291auto 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
307template <typename... Args>
308void MaybeBuildField(std::true_type, Args&&... args)
309{
310 BuildField(std::forward<Args>(args)...);
311}
312template <typename... Args>
313void MaybeBuildField(std::false_type, Args&&...)
314{
315}
316template <typename... Args>
317void MaybeReadField(std::true_type, Args&&... args)
318{
319 ReadField(std::forward<Args>(args)...);
320}
321template <typename... Args>
322void MaybeReadField(std::false_type, Args&&...)
323{
324}
325
326template <typename LocalType, typename Value, typename Output>
327void MaybeSetWant(TypeList<LocalType*>, Priority<1>, const Value& value, Output&& output)
328{
329 if (value) {
330 output.setWant();
331 }
332}
333
334template <typename LocalTypes, typename... Args>
335void MaybeSetWant(LocalTypes, Priority<0>, const Args&...)
336{
337}
338
340template <typename Accessor, typename LocalType, typename ServerContext, typename Fn, typename... Args>
341void 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
364template <typename Accessor, typename ServerContext, typename Fn, typename... Args>
365void 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
375template <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 }
392private:
394 friend Derived;
395};
396
397struct IterateFields : IterateFieldsHelper<IterateFields, 0>
398{
399 template <typename Arg1, typename Arg2, typename ParamList>
400 void handleField(Arg1&&, Arg2&&, ParamList)
401 {
402 }
403};
404
405template <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
435template <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) {
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
486template <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
540template <typename Accessor, typename Parent>
541struct 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
556template <typename Exception, typename Accessor, typename Parent>
557struct 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
575template <typename Accessor, typename Message>
576decltype(auto) MaybeGet(Message&& message, decltype(Accessor::get(message))* enable = nullptr)
577{
578 return Accessor::get(message);
579}
580
581template <typename Accessor>
582::capnp::Void MaybeGet(...)
583{
584 return {};
585}
586
587template <class Accessor>
589
600template <typename Accessor, typename... Args>
601auto PassField(Priority<2>, Args&&... args) -> decltype(CustomPassField<Accessor>(std::forward<Args>(args)...))
602{
603 return CustomPassField<Accessor>(std::forward<Args>(args)...);
604};
605
606template <int argc, typename Accessor, typename Parent>
607struct 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>(),
618 server_context,
619 this->parent(),
621 std::forward<Args>(args)...);
622 }
623};
624
625template <int argc, typename Accessor, typename Parent>
627{
628 return {parent};
629}
630
631template <typename Request>
633
634template <typename _Params, typename _Results>
635struct CapRequestTraits<::capnp::Request<_Params, _Results>>
636{
637 using Params = _Params;
638 using Results = _Results;
639};
640
644template <typename Client>
645void 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
654template <typename Server>
655void serverDestroy(Server& server)
656{
657 MP_LOG(*server.m_context.loop, Log::Debug) << "IPC server destroy " << typeid(server).name();
658}
659
669template <typename ProxyClient, typename GetRequest, typename... FieldObjs>
670void clientInvoke(ProxyClient& proxy_client, const GetRequest& get_request, FieldObjs&&... fields)
671{
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)
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 {
723 IterateFields().handleChain(
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
756template <typename Fn, typename Ret>
757auto 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
767extern std::atomic<int> server_reqs;
768
776template <typename Server, typename CallContext, typename Fn>
777kj::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
int ret
catch(const std::exception &e)
std::unique_ptr< interfaces::Init > init
ArgsManager & args
Definition: bitcoind.cpp:278
const CChainParams & Params()
Return the currently selected parameters.
Definition: util.h:171
std::unique_lock< std::mutex > m_lock
Definition: util.h:183
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
Functions to serialize / deserialize common bitcoin types.
Definition: common-types.h:57
void MaybeBuildField(std::true_type, Args &&... args)
Definition: proxy-types.h:308
void clientDestroy(Client &client)
Entry point called by all generated ProxyClient destructors.
Definition: proxy-types.h:645
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
bool CustomHasField(TypeList< LocalTypes... >, InvokeContext &invoke_context, const Input &input)
Return whether to read a C++ value from a Cap'n Proto field.
Definition: proxy-types.h:207
void CustomPassField()
ClientParam< Accessor, Types... > MakeClientParam(Types &&... values)
Definition: proxy-types.h:487
void MaybeReadField(std::true_type, Args &&... args)
Definition: proxy-types.h:317
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
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 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:291
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:97
std::atomic< int > server_reqs
Definition: proxy.cpp:434
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:72
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
thread_local ThreadContext g_thread_context
Definition: proxy.cpp:41
std::decay_t< T > Decay
Type helper abbreviating std::decay.
Definition: util.h:86
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:82
decltype(auto) ReadField(TypeList< LocalTypes... >, InvokeContext &invoke_context, Input &&input, Args &&... args)
Definition: proxy-types.h:213
char * CharCast(char *c)
Definition: util.h:272
decltype(auto) MaybeGet(Message &&message, decltype(Accessor::get(message)) *enable=nullptr)
Helper for CustomPassField below.
Definition: proxy-types.h:576
auto ReplaceVoid(Fn &&fn, Ret &&ret)
Invoke callable fn() that may return void.
Definition: proxy-types.h:757
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:626
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
Definition: common.h:29
#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:209
#define MP_LOGPLAIN(loop,...)
Definition: proxy-io.h:207
const char * name
Definition: rest.cpp:49
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
Definition: proxy.h:321
static const bool optional
Definition: proxy.h:319
static const bool requested
Definition: proxy.h:320
static const bool in
Definition: proxy.h:317
void handleField(InvokeContext &invoke_context, Params &params, ParamList)
Definition: proxy-types.h:411
ClientException * m_client_exception
Definition: proxy-types.h:416
BuildParams(ClientException *client_exception)
Definition: proxy-types.h:415
void handleField(InvokeContext &invoke_context, Results &results, ParamList)
Definition: proxy-types.h:422
ReadResults(ClientException *client_exception)
Definition: proxy-types.h:430
ClientException * m_client_exception
Definition: proxy-types.h:431
void handleField(ClientInvokeContext &invoke_context, Params &params, ParamList)
Definition: proxy-types.h:443
BuildParams(ClientParam *client_param)
Definition: proxy-types.h:462
ReadResults(ClientParam *client_param)
Definition: proxy-types.h:479
void handleField(ClientInvokeContext &invoke_context, Results &results, TypeList< Params... >)
Definition: proxy-types.h:469
ClientParam(Types &&... values)
Definition: proxy-types.h:438
std::tuple< Types &&... > m_values
Definition: proxy-types.h:483
Exception thrown from code executing an IPC call that is interrupted.
Definition: util.h:278
void handleChain(Arg1 &arg1, Arg2 &arg2, ParamList)
Definition: proxy-types.h:388
void handleChain(Arg1 &arg1, Arg2 &arg2, ParamList, NextFn &&next_fn, NextFnArgs &&... next_fn_args)
Definition: proxy-types.h:379
void handleField(Arg1 &&, Arg2 &&, ParamList)
Definition: proxy-types.h:400
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
Specialization of above (base case)
Definition: util.h:115
Function parameter type for prioritizing overloaded function calls that would otherwise be ambiguous.
Definition: util.h:109
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:818
static Types & types()
Definition: proxy-types.h:824
ProxyTypeRegister(TypeList< Interface >)
Definition: proxy-types.h:820
std::map< std::type_index, ProxyContext &(*)(void *)> Types
Definition: proxy-types.h:823
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:496
void invoke(ServerContext &server_context, TypeList<>, Args &&... args) const
Definition: proxy-types.h:534
ServerExcept(Parent parent)
Definition: proxy-types.h:559
void invoke(ServerContext &server_context, TypeList<>, Args &&... args) const
Definition: proxy-types.h:562
ServerField(Parent parent)
Definition: proxy-types.h:609
decltype(auto) invoke(ServerContext &server_context, ArgTypes, Args &&... args) const
Definition: proxy-types.h:614
const Parent & parent() const
Definition: proxy-types.h:611
CallContext & call_context
Definition: proxy-io.h:49
ProxyServer & proxy_server
Definition: proxy-io.h:48
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:63
Lock * cancel_lock
For IPC methods that execute asynchronously, not on the event-loop thread: lock preventing the event-...
Definition: proxy-io.h:56
void invoke(ServerContext &server_context, TypeList<>, Args &&... args) const
Definition: proxy-types.h:546
ServerRet(Parent parent)
Definition: proxy-types.h:543
Type helper splitting a TypeList into two halves at position index.
Definition: util.h:57
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:675
std::unique_ptr< Waiter > waiter
Waiter object used to allow remote clients to execute code on this thread.
Definition: proxy-io.h:694
bool loop_thread
Whether this thread is a capnp event loop thread.
Definition: proxy-io.h:729
std::string thread_name
Identifying string for debug.
Definition: proxy-io.h:677
Generic utility functions used by capnp code.
Definition: util.h:33
#define B
Definition: util_tests.cpp:561
assert(!tx.IsCoinBase())