5 #ifndef MP_PROXY_TYPES_H 6 #define MP_PROXY_TYPES_H 18 template <
typename Value>
26 const Value&
get()
const {
return m_value; }
29 bool has()
const {
return true; }
32 template <
typename Accessor,
typename Struct>
41 decltype(
auto)
get()
const {
return Accessor::get(this->m_struct); }
61 template <
typename... Args> decltype(
auto)
set(Args &&...
args)
const {
62 return Accessor::set(this->m_struct, std::forward<Args>(
args)...);
65 template <
typename... Args> decltype(
auto)
init(Args &&...
args)
const {
93 template <
typename LocalType,
typename EmplaceFn>
100 template <
typename... Args>
110 template <
typename UpdateFn>
111 decltype(
auto)
update(UpdateFn&& update_fn)
113 if constexpr (std::is_const_v<std::remove_reference_t<std::invoke_result_t<EmplaceFn>>>) {
117 std::remove_cv_t<LocalType> temp;
132 template <
typename LocalType>
136 return LocalType{std::forward<decltype(args)>(
args)...};
144 template <
typename Value>
150 template <
typename UpdateFn>
159 template <
typename... Args>
206 template <
typename... LocalTypes,
typename Input>
212 template <
typename... LocalTypes,
typename Input,
typename... Args>
218 template <
typename LocalType,
typename Input>
223 [](
auto&& ...
args) ->
const LocalType& {
throw LocalType{std::forward<decltype(args)>(
args)...}; }));
229 template <
typename Input>
232 auto data = input.get();
243 template <
typename... Values>
249 template <
typename... LocalTypes,
typename Context,
typename... Values,
typename Output>
254 std::forward<Output>(output));
263 template <
typename ListType>
266 template <
typename T, ::capnp::Kind kind>
269 using Builder = typename ::capnp::List<T, kind>::Builder;
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)); }
283 template <
typename LocalType,
typename Value,
typename Output>
286 output.set(
BuildPrimitive(invoke_context, std::forward<Value>(value),
TypeList<decltype(output.get())>()));
290 template <
typename Accessor,
typename LocalType,
typename ServerContext,
typename Fn,
typename... Args>
292 ->
Require<
typename decltype(Accessor::get(server_context.call_context.getParams()))::Calls>
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);
307 template <
typename... Args>
312 template <
typename... Args>
316 template <
typename... Args>
321 template <
typename... Args>
326 template <
typename LocalType,
typename Value,
typename Output>
334 template <
typename LocalTypes,
typename... Args>
340 template <
typename Accessor,
typename LocalType,
typename ServerContext,
typename Fn,
typename... Args>
345 std::optional<ArgType> param;
346 const auto& params = server_context.
call_context.getParams();
349 param.emplace(std::forward<decltype(
args)>(
args)...);
355 if (!param) param.emplace();
357 fn.invoke(server_context, std::forward<Args>(
args)..., static_cast<LocalType&&>(*param));
358 auto&& results = server_context.
call_context.getResults();
360 Make<StructField, Accessor>(results), *param);
364 template <
typename Accessor,
typename ServerContext,
typename Fn,
typename... Args>
367 const auto& params = server_context.
call_context.getParams();
368 const auto& input = Make<StructField, Accessor>(params);
370 fn.invoke(server_context, std::forward<Args>(
args)...);
371 auto&& results = server_context.
call_context.getResults();
375 template <
typename Derived,
size_t N = 0>
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)
383 next_fn.handleChain(arg1, arg2,
typename S::Second(),
384 std::forward<NextFnArgs>(next_fn_args)...);
387 template <
typename Arg1,
typename Arg2,
typename ParamList>
390 static_cast<Derived*
>(
this)->handleField(arg1, arg2, ParamList());
399 template <
typename Arg1,
typename Arg2,
typename ParamList>
405 template <
typename Exception,
typename Accessor>
410 template <
typename Params,
typename ParamList>
421 template <
typename Results,
typename ParamList>
435 template <
typename Accessor,
typename... Types>
442 template <
typename Params,
typename ParamList>
445 auto const fun = [&]<
typename... Values>(Values&&...
values) {
448 MaybeBuildField(std::integral_constant<bool, Accessor::in>(), ParamList(), invoke_context,
449 Make<StructField, Accessor>(params), std::forward<Values>(
values)...);
468 template <
typename Results,
typename...
Params>
471 auto const fun = [&]<
typename... Values>(Values&&...
values) {
486 template <
typename Accessor,
typename... Types>
489 return {std::forward<Types>(
values)...};
505 if (server_context.cancel_lock) server_context.cancel_lock->m_lock.unlock();
507 [&]() -> decltype(
auto) {
509 typename decltype(server_context.call_context.getParams())::Reads
510 >::
invoke(server_context, std::forward<Args>(
args)...);
513 if (server_context.cancel_lock) server_context.cancel_lock->m_lock.lock();
540 template <
typename Accessor,
typename Parent>
549 auto&& results = server_context.
call_context.getResults();
556 template <
typename Exception,
typename Accessor,
typename Parent>
565 return Parent::invoke(server_context,
TypeList<>(), std::forward<Args>(
args)...);
566 }
catch (
const Exception& exception) {
567 auto&& results = server_context.
call_context.getResults();
575 template <
typename Accessor,
typename Message>
576 decltype(
auto)
MaybeGet(Message&& message, decltype(
Accessor::get(message))* enable =
nullptr)
578 return Accessor::get(message);
581 template <
typename Accessor>
587 template <
class Accessor>
600 template <
typename Accessor,
typename... Args>
603 return CustomPassField<Accessor>(std::forward<Args>(
args)...);
606 template <
int argc,
typename Accessor,
typename Parent>
611 const Parent&
parent()
const {
return *
this; }
613 template <
typename ServerContext,
typename ArgTypes,
typename... Args>
621 std::forward<Args>(
args)...);
625 template <
int argc,
typename Accessor,
typename Parent>
631 template <
typename Request>
634 template <
typename _Params,
typename _Results>
644 template <
typename Client>
647 if (client.m_context.connection) {
650 KJ_LOG(INFO,
"IPC interrupted client destroy",
typeid(client).
name());
654 template <
typename Server>
669 template <
typename ProxyClient,
typename GetRequest,
typename... FieldObjs>
687 <<
"} IPC client first request from current thread, constructing waiter";
690 std::optional<ClientInvokeContext> invoke_context;
691 std::exception_ptr exception;
692 std::string kj_exception;
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);
699 disconnected =
"IPC client method called after disconnect.";
700 thread_context.waiter->m_cv.notify_all();
704 auto request = (proxy_client.m_client.*get_request)(
nullptr);
707 invoke_context.emplace(*proxy_client.m_context.connection, thread_context);
710 <<
"{" << thread_context.thread_name <<
"} IPC client send " 711 << TypeName<typename Request::Params>();
713 <<
"send data: " <<
LogEscape(request.toString(), proxy_client.m_context.loop->m_log_opts.max_chars);
715 proxy_client.m_context.loop->m_task_set->add(request.send().then(
716 [&](::capnp::Response<typename Request::Results>&& response) {
718 <<
"{" << thread_context.thread_name <<
"} IPC client recv " 719 << TypeName<typename Request::Results>();
721 <<
"recv data: " <<
LogEscape(response.toString(), proxy_client.m_context.loop->m_log_opts.max_chars);
724 *invoke_context, response, FieldList(),
typename FieldObjs::ReadResults{&fields}...);
726 exception = std::current_exception();
728 const Lock lock(thread_context.waiter->m_mutex);
730 thread_context.waiter->m_cv.notify_all();
732 [&](const ::kj::Exception& e) {
733 if (e.getType() == ::kj::Exception::Type::DISCONNECTED) {
734 disconnected =
"IPC client method call interrupted by disconnect.";
736 kj_exception = kj::str(
"kj::Exception: ", e).cStr();
738 <<
"{" << thread_context.thread_name <<
"} IPC client exception " << kj_exception;
740 const Lock lock(thread_context.waiter->m_mutex);
742 thread_context.waiter->m_cv.notify_all();
746 Lock lock(thread_context.waiter->m_mutex);
747 thread_context.waiter->wait(lock, [&done]() {
return done; });
748 if (exception) std::rethrow_exception(exception);
756 template <
typename Fn,
typename Ret>
759 if constexpr (std::is_same_v<decltype(fn()),
void>) {
776 template <
typename Server,
typename CallContext,
typename Fn>
777 kj::Promise<void>
serverInvoke(Server& server, CallContext& call_context, Fn fn)
779 auto params = call_context.getParams();
780 using Params = decltype(params);
781 using Results =
typename decltype(call_context.getResults())::Builds;
784 MP_LOG(*server.m_context.loop,
Log::Debug) <<
"IPC server recv request #" << req <<
" " 785 << TypeName<typename Params::Reads>();
787 <<
LogEscape(params.toString(), server.m_context.loop->m_log_opts.max_chars);
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>();
805 <<
LogEscape(call_context.getResults().toString(), server.m_context.loop->m_log_opts.max_chars);
807 }
catch (
const std::exception& e) {
808 MP_LOG(*server.m_context.loop,
Log::Error) <<
"IPC server unhandled exception: " << e.what();
811 MP_LOG(*server.m_context.loop,
Log::Error) <<
"IPC server unhandled exception";
819 template<
typename Interface>
823 using Types = std::map<std::type_index, ProxyContext&(*)(void*)>;
829 #endif // MP_PROXY_TYPES_H ClientParam< Accessor, Types... > MakeClientParam(Types &&... values)
void handleChain(Arg1 &arg1, Arg2 &arg2, ParamList)
std::tuple< Types &&... > m_values
void clientInvoke(ProxyClient &proxy_client, const GetRequest &get_request, FieldObjs &&... fields)
Entry point called by generated client code that looks like:
ReadDestEmplace(TypeList< LocalType >, EmplaceFn emplace_fn)
auto ReplaceVoid(Fn &&fn, Ret &&ret)
Invoke callable fn() that may return void.
Function parameter type for prioritizing overloaded function calls that would otherwise be ambiguous...
The thread_local ThreadContext g_thread_context struct provides information about individual threads ...
std::remove_cv_t< std::remove_reference_t< T > > RemoveCvRef
Substitutue for std::remove_cvref_t.
std::string ThreadName(const char *exe_name)
Format current thread name as "{exe_name}-{$pid}/{thread_name}-{$tid}".
bool CustomHasField(TypeList< LocalTypes... >, InvokeContext &invoke_context, const Input &input)
Return whether to read a C++ value from a Cap'n Proto field.
std::atomic< int > server_reqs
#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...
Accessor type holding flags that determine how to access a message field.
ListOutput(Builder &builder, size_t index)
void handleField(InvokeContext &invoke_context, Params ¶ms, ParamList)
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.
void invoke(ServerContext &server_context, TypeList<>, Args &&... args) const
ServerField< argc, Accessor, Parent > MakeServerField(Parent parent)
Generic utility functions used by capnp code.
Customizable (through template specialization) traits class used in generated ProxyServer implementat...
ClientParam(Types &&... values)
ReadResults(ClientException *client_exception)
void handleField(Arg1 &&, Arg2 &&, ParamList)
void clientDestroy(Client &client)
Entry point called by all generated ProxyClient destructors.
Destination parameter type that can be passed to ReadField function as an alternative to ReadDestEmpl...
Specialization of above (base case)
ProxyTypeRegister(TypeList< Interface >)
Value & update(UpdateFn &&update_fn)
Simple case. If ReadField works by calling update() just forward arguments to update_fn.
typename ::capnp::List< T, kind >::Builder Builder
Functions to serialize / deserialize common bitcoin types.
Context data associated with proxy client and server classes.
ClientParam * m_client_param
decltype(auto) get() const
ReadDestUpdate(Value &value)
kj::Promise< void > serverInvoke(Server &server, CallContext &call_context, Fn fn)
Entry point called by generated server code that looks like:
void MaybeSetWant(TypeList< LocalType *>, Priority< 1 >, const Value &value, Output &&output)
void serverDestroy(Server &server)
ServerExcept(Parent parent)
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...
thread_local ThreadContext g_thread_context
bool loop_thread
Whether this thread is a capnp event loop thread.
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)
ServerInvokeContext< ProxyServer< Interface >, ::capnp::CallContext< Params, Results > > ServerContext
std::map< std::type_index, ProxyContext &(*)(void *)> Types
Type helper splitting a TypeList into two halves at position index.
decltype(auto) invoke(ServerContext &server_context, TypeList<>, Args &&... args) const
ProxyServer & proxy_server
Map to convert client interface pointers to ProxyContext struct references at runtime using typeids...
ValueField(Value &&value)
Mapping from local c++ type to capnp type and traits (specializations are generated by proxy-codegen...
ServerField(Parent parent)
ReadResults(ClientParam *client_param)
std::string thread_name
Identifying string for debug.
void BuildField(TypeList< LocalTypes... >, Context &context, Output &&output, Values &&... values)
static const bool requested
void MaybeReadField(std::true_type, Args &&... args)
void invoke(ServerContext &server_context, TypeList<>, Args &&... args) const
ClientException * m_client_exception
decltype(auto) TryFinally(Fn &&fn, After &&after)
Invoke a function and run a follow-up action before returning the original result.
decltype(auto) MaybeGet(Message &&message, decltype(Accessor::get(message)) *enable=nullptr)
Helper for CustomPassField below.
decltype(auto) update(UpdateFn &&update_fn)
More complicated case.
#define MP_LOGPLAIN(loop,...)
decltype(auto) CustomReadField(TypeList< LocalType >, Priority< 1 >, InvokeContext &invoke_context, Input &&input, ReadDest &&read_dest) requires Unserializable< LocalType
Overload multiprocess library's CustomReadField hook to allow any object with an Unserialize method t...
static const bool optional
void CustomBuildField(TypeList< LocalType >, Priority< 1 >, InvokeContext &invoke_context, Value &&value, Output &&output) requires Serializable< LocalType
Overload multiprocess library's CustomBuildField hook to allow any serializable object to be stored i...
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...
const CChainParams & Params()
Return the currently selected parameters.
BuildParams(ClientParam *client_param)
std::decay_t< T > Decay
Type helper abbreviating std::decay.
decltype(auto) ReadField(TypeList< LocalTypes... >, InvokeContext &invoke_context, Input &&input, Args &&... args)
ClientParam * m_client_param
decltype(auto) init(Arg &&arg) const
void ThrowField(TypeList< LocalType >, InvokeContext &invoke_context, Input &&input)
void handleField(ClientInvokeContext &invoke_context, Params ¶ms, ParamList)
ClientException * m_client_exception
BuildParams(ClientException *client_exception)
decltype(auto) set(Args &&...args) const
std::unique_ptr< Waiter > waiter
Waiter object used to allow remote clients to execute code on this thread.
void invoke(ServerContext &server_context, TypeList<>, Args &&... args) const
Value & construct(Args &&... args)
More complicated case.
CallContext & call_context
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.
bool CustomHasValue(InvokeContext &invoke_context, const Values &... value)
Return whether to write a C++ value into a Cap'n Proto field.
void handleField(ClientInvokeContext &invoke_context, Results &results, TypeList< Params... >)
IterateFieldsHelper()=default
decltype(auto) invoke(ServerContext &server_context, ArgTypes, Args &&... args) const
void handleField(InvokeContext &invoke_context, Results &results, ParamList)
void handleChain(Arg1 &arg1, Arg2 &arg2, ParamList, NextFn &&next_fn, NextFnArgs &&... next_fn_args)
Mapping from capnp interface type to proxy client implementation (specializations are generated by pr...
const Parent & parent() const
void MaybeBuildField(std::true_type, Args &&... args)
decltype(auto) construct(Args &&... args)
Simple case.