5#ifndef MP_PROXY_TYPES_H
6#define MP_PROXY_TYPES_H
18template <
typename Value>
29 bool has()
const {
return true; }
32template <
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 {
93template <
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;
132template <
typename LocalType>
136 return LocalType{std::forward<decltype(args)>(
args)...};
144template <
typename Value>
150 template <
typename UpdateFn>
159 template <
typename... Args>
206template <
typename... LocalTypes,
typename Input>
212template <
typename... LocalTypes,
typename Input,
typename... Args>
218template <
typename LocalType,
typename Input>
223 [](
auto&& ...
args) ->
const LocalType& {
throw LocalType{std::forward<decltype(args)>(
args)...}; }));
229template <
typename Input>
232 auto data = input.get();
243template <
typename... Values>
249template <
typename... LocalTypes,
typename Context,
typename... Values,
typename Output>
254 std::forward<Output>(output));
263template <
typename ListType>
266template <
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)); }
283template <
typename LocalType,
typename Value,
typename Output>
286 output.set(
BuildPrimitive(invoke_context, std::forward<Value>(value),
TypeList<
decltype(output.get())>()));
290template <
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);
307template <
typename... Args>
312template <
typename... Args>
316template <
typename... Args>
321template <
typename... Args>
326template <
typename LocalType,
typename Value,
typename Output>
334template <
typename LocalTypes,
typename... Args>
340template <
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);
364template <
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();
375template <
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>
405template <
typename Exception,
typename Accessor>
410 template <
typename Params,
typename ParamList>
421 template <
typename Results,
typename ParamList>
435template <
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) {
486template <
typename Accessor,
typename... Types>
489 return {std::forward<Types>(
values)...};
507 [&]() ->
decltype(
auto) {
509 typename decltype(server_context.
call_context.getParams())::Reads
540template <
typename Accessor,
typename Parent>
548 auto&& result = Parent::invoke(server_context,
TypeList<>(), std::forward<Args>(
args)...);
549 auto&& results = server_context.
call_context.getResults();
551 BuildField(
TypeList<
decltype(result)>(), invoke_context, Make<StructField, Accessor>(results),
552 std::forward<
decltype(result)>(result));
556template <
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();
575template <
typename Accessor,
typename Message>
576decltype(
auto)
MaybeGet(Message&& message,
decltype(Accessor::get(message))* enable =
nullptr)
578 return Accessor::get(message);
581template <
typename Accessor>
587template <
class Accessor>
600template <
typename Accessor,
typename... Args>
603 return CustomPassField<Accessor>(std::forward<Args>(
args)...);
606template <
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)...);
625template <
int argc,
typename Accessor,
typename Parent>
631template <
typename Request>
634template <
typename _Params,
typename _Results>
644template <
typename Client>
647 if (client.m_context.connection) {
650 KJ_LOG(INFO,
"IPC interrupted client destroy",
typeid(client).
name());
654template <
typename Server>
669template <
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);
708 IterateFields().handleChain(*invoke_context, request, FieldList(),
typename FieldObjs::BuildParams{&fields}...);
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) {
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);
723 IterateFields().handleChain(
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);
756template <
typename Fn,
typename Ret>
759 if constexpr (std::is_same_v<
decltype(fn()),
void>) {
776template <
typename Server,
typename CallContext,
typename Fn>
777kj::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>();
786 MP_LOG(*server.m_context.loop, Log::Trace) <<
"request data: "
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>();
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);
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>
catch(const std::exception &e)
std::unique_ptr< interfaces::Init > init
const CChainParams & Params()
Return the currently selected parameters.
std::unique_lock< std::mutex > m_lock
const Value & get() const
ValueField(Value &&value)
Functions to serialize / deserialize common bitcoin types.
void MaybeBuildField(std::true_type, Args &&... args)
void clientDestroy(Client &client)
Entry point called by all generated ProxyClient destructors.
void clientInvoke(ProxyClient &proxy_client, const GetRequest &get_request, FieldObjs &&... fields)
Entry point called by generated client code that looks like:
bool CustomHasField(TypeList< LocalTypes... >, InvokeContext &invoke_context, const Input &input)
Return whether to read a C++ value from a Cap'n Proto field.
ClientParam< Accessor, Types... > MakeClientParam(Types &&... values)
void MaybeReadField(std::true_type, Args &&... args)
decltype(auto) TryFinally(Fn &&fn, After &&after)
Invoke a function and run a follow-up action before returning the original result.
kj::Promise< void > serverInvoke(Server &server, CallContext &call_context, Fn fn)
Entry point called by generated server code that looks like:
void BuildField(TypeList< LocalTypes... >, Context &context, Output &&output, Values &&... values)
bool CustomHasValue(InvokeContext &invoke_context, const Values &... value)
Return whether to write a C++ value into a Cap'n Proto field.
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.
std::string ThreadName(const char *exe_name)
Format current thread name as "{exe_name}-{$pid}/{thread_name}-{$tid}".
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...
LocalType BuildPrimitive(InvokeContext &invoke_context, const Value &value, TypeList< LocalType >, typename std::enable_if< std::is_enum< Value >::value >::type *enable=nullptr)
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...
std::atomic< int > server_reqs
void ThrowField(TypeList< LocalType >, InvokeContext &invoke_context, Input &&input)
ServerInvokeContext< ProxyServer< Interface >, ::capnp::CallContext< Params, Results > > ServerContext
void MaybeSetWant(TypeList< LocalType * >, Priority< 1 >, const Value &value, Output &&output)
void serverDestroy(Server &server)
thread_local ThreadContext g_thread_context
std::decay_t< T > Decay
Type helper abbreviating std::decay.
auto ReadDestTemp()
Helper function to create a ReadDestEmplace object that constructs a temporary, ReadField can return.
std::remove_cv_t< std::remove_reference_t< T > > RemoveCvRef
Substitutue for std::remove_cvref_t.
decltype(auto) ReadField(TypeList< LocalTypes... >, InvokeContext &invoke_context, Input &&input, Args &&... args)
decltype(auto) MaybeGet(Message &&message, decltype(Accessor::get(message)) *enable=nullptr)
Helper for CustomPassField below.
auto ReplaceVoid(Fn &&fn, Ret &&ret)
Invoke callable fn() that may return void.
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...
ServerField< argc, Accessor, Parent > MakeServerField(Parent parent)
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.
#define S(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p)
#define MP_LOGPLAIN(loop,...)
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.
static const bool optional
static const bool requested
void handleField(InvokeContext &invoke_context, Params ¶ms, ParamList)
ClientException * m_client_exception
BuildParams(ClientException *client_exception)
void handleField(InvokeContext &invoke_context, Results &results, ParamList)
ReadResults(ClientException *client_exception)
ClientException * m_client_exception
void handleField(ClientInvokeContext &invoke_context, Params ¶ms, ParamList)
BuildParams(ClientParam *client_param)
ClientParam * m_client_param
ClientParam * m_client_param
ReadResults(ClientParam *client_param)
void handleField(ClientInvokeContext &invoke_context, Results &results, TypeList< Params... >)
ClientParam(Types &&... values)
std::tuple< Types &&... > m_values
Exception thrown from code executing an IPC call that is interrupted.
void handleChain(Arg1 &arg1, Arg2 &arg2, ParamList)
void handleChain(Arg1 &arg1, Arg2 &arg2, ParamList, NextFn &&next_fn, NextFnArgs &&... next_fn_args)
IterateFieldsHelper()=default
void handleField(Arg1 &&, Arg2 &&, ParamList)
decltype(auto) set(Arg &&arg) const
decltype(auto) init(Arg &&arg) const
decltype(auto) get() const
decltype(auto) init() const
typename ::capnp::List< T, kind >::Builder Builder
ListOutput(Builder &builder, size_t index)
Specialization of above (base case)
Function parameter type for prioritizing overloaded function calls that would otherwise be ambiguous.
Mapping from capnp interface type to proxy client implementation (specializations are generated by pr...
Context data associated with proxy client and server classes.
Customizable (through template specialization) traits class used in generated ProxyServer implementat...
Mapping from local c++ type to capnp type and traits (specializations are generated by proxy-codegen....
Map to convert client interface pointers to ProxyContext struct references at runtime using typeids.
ProxyTypeRegister(TypeList< Interface >)
std::map< std::type_index, ProxyContext &(*)(void *)> Types
decltype(auto) construct(Args &&... args)
Simple case.
ReadDestEmplace(TypeList< LocalType >, EmplaceFn emplace_fn)
decltype(auto) update(UpdateFn &&update_fn)
More complicated case.
Destination parameter type that can be passed to ReadField function as an alternative to ReadDestEmpl...
Value & update(UpdateFn &&update_fn)
Simple case. If ReadField works by calling update() just forward arguments to update_fn.
Value & construct(Args &&... args)
More complicated case.
ReadDestUpdate(Value &value)
decltype(auto) invoke(ServerContext &server_context, TypeList<>, Args &&... args) const
void invoke(ServerContext &server_context, TypeList<>, Args &&... args) const
ServerExcept(Parent parent)
void invoke(ServerContext &server_context, TypeList<>, Args &&... args) const
ServerField(Parent parent)
decltype(auto) invoke(ServerContext &server_context, ArgTypes, Args &&... args) const
const Parent & parent() const
CallContext & call_context
ProxyServer & proxy_server
bool request_canceled
For IPC methods that execute asynchronously, not on the event-loop thread, this is set to true if the...
Lock * cancel_lock
For IPC methods that execute asynchronously, not on the event-loop thread: lock preventing the event-...
void invoke(ServerContext &server_context, TypeList<>, Args &&... args) const
Type helper splitting a TypeList into two halves at position index.
decltype(auto) set(Args &&...args) const
decltype(auto) init(Args &&...args) const
decltype(auto) get() const
The thread_local ThreadContext g_thread_context struct provides information about individual threads ...
std::unique_ptr< Waiter > waiter
Waiter object used to allow remote clients to execute code on this thread.
bool loop_thread
Whether this thread is a capnp event loop thread.
std::string thread_name
Identifying string for debug.
Generic utility functions used by capnp code.