5#ifndef MP_PROXY_TYPES_H
6#define MP_PROXY_TYPES_H
18template <
typename Value>
28 bool has() {
return true; }
31template <
typename Accessor,
typename Struct>
40 decltype(
auto)
get()
const {
return Accessor::get(this->m_struct); }
60 template <
typename... Args>
decltype(
auto)
set(Args &&...
args)
const {
61 return Accessor::set(this->m_struct, std::forward<Args>(
args)...);
64 template <
typename... Args>
decltype(
auto)
init(Args &&...
args)
const {
92template <
typename LocalType,
typename EmplaceFn>
99 template <
typename... Args>
109 template <
typename UpdateFn>
110 decltype(
auto)
update(UpdateFn&& update_fn)
112 if constexpr (std::is_const_v<std::remove_reference_t<std::invoke_result_t<EmplaceFn>>>) {
116 std::remove_cv_t<LocalType> temp;
131template <
typename LocalType>
135 return LocalType{std::forward<decltype(args)>(
args)...};
143template <
typename Value>
149 template <
typename UpdateFn>
158 template <
typename... Args>
169template <
typename... LocalTypes,
typename... Args>
175template <
typename LocalType,
typename Input>
180 [](
auto&& ...
args) ->
const LocalType& {
throw LocalType{std::forward<decltype(args)>(
args)...}; }));
186template <
typename Input>
189 auto data = input.get();
193template <
typename... Values>
199template <
typename... LocalTypes,
typename Context,
typename... Values,
typename Output>
204 std::forward<Output>(output));
213template <
typename ListType>
216template <
typename T, ::capnp::Kind kind>
219 using Builder = typename ::capnp::List<T, kind>::Builder;
226 decltype(
auto)
get()
const {
return this->m_builder[this->m_index]; }
227 decltype(
auto)
init()
const {
return this->m_builder[this->m_index]; }
228 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)); }
229 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)); }
233template <
typename LocalType,
typename Value,
typename Output>
236 output.set(
BuildPrimitive(invoke_context, std::forward<Value>(value),
TypeList<
decltype(output.get())>()));
240template <
typename Accessor,
typename LocalType,
typename ServerContext,
typename Fn,
typename... Args>
242 ->
Require<
typename decltype(Accessor::get(server_context.call_context.getParams()))::Calls>
250 const auto& params = server_context.call_context.getParams();
251 const auto& input = Make<StructField, Accessor>(params);
252 using Interface =
typename Decay<
decltype(input.get())>::Calls;
253 auto param = std::make_unique<ProxyClient<Interface>>(input.get(), server_context.proxy_server.m_context.connection,
false);
254 fn.invoke(server_context, std::forward<Args>(
args)..., *param);
257template <
typename... Args>
262template <
typename... Args>
266template <
typename... Args>
271template <
typename... Args>
276template <
typename LocalType,
typename Value,
typename Output>
284template <
typename LocalTypes,
typename... Args>
290template <
typename Accessor,
typename LocalType,
typename ServerContext,
typename Fn,
typename... Args>
295 std::optional<ArgType> param;
296 const auto& params = server_context.
call_context.getParams();
299 param.emplace(std::forward<
decltype(
args)>(
args)...);
305 if (!param) param.emplace();
307 fn.invoke(server_context, std::forward<Args>(
args)...,
static_cast<LocalType&&
>(*param));
308 auto&& results = server_context.
call_context.getResults();
310 Make<StructField, Accessor>(results), *param);
314template <
typename Accessor,
typename ServerContext,
typename Fn,
typename... Args>
317 const auto& params = server_context.
call_context.getParams();
318 const auto& input = Make<StructField, Accessor>(params);
320 fn.invoke(server_context, std::forward<Args>(
args)...);
321 auto&& results = server_context.
call_context.getResults();
325template <
typename Derived,
size_t N = 0>
328 template <
typename Arg1,
typename Arg2,
typename ParamList,
typename NextFn,
typename... NextFnArgs>
329 void handleChain(Arg1& arg1, Arg2& arg2, ParamList, NextFn&& next_fn, NextFnArgs&&... next_fn_args)
333 next_fn.handleChain(arg1, arg2,
typename S::Second(),
334 std::forward<NextFnArgs>(next_fn_args)...);
337 template <
typename Arg1,
typename Arg2,
typename ParamList>
340 static_cast<Derived*
>(
this)->handleField(arg1, arg2, ParamList());
349 template <
typename Arg1,
typename Arg2,
typename ParamList>
355template <
typename Exception,
typename Accessor>
360 template <
typename Params,
typename ParamList>
371 template <
typename Results,
typename ParamList>
385template <
typename Accessor,
typename... Types>
392 template <
typename Params,
typename ParamList>
395 auto const fun = [&]<
typename... Values>(Values&&...
values) {
398 MaybeBuildField(std::integral_constant<bool, Accessor::in>(), ParamList(), invoke_context,
399 Make<StructField, Accessor>(params), std::forward<Values>(
values)...);
418 template <
typename Results,
typename...
Params>
421 auto const fun = [&]<
typename... Values>(Values&&...
values) {
436template <
typename Accessor,
typename... Types>
439 return {std::forward<Types>(
values)...};
450 std::forward<Args>(
args)...);
463template <
typename Accessor,
typename Parent>
471 auto&& result = Parent::invoke(server_context,
TypeList<>(), std::forward<Args>(
args)...);
472 auto&& results = server_context.
call_context.getResults();
474 BuildField(
TypeList<
decltype(result)>(), invoke_context, Make<StructField, Accessor>(results),
475 std::forward<
decltype(result)>(result));
479template <
typename Exception,
typename Accessor,
typename Parent>
488 return Parent::invoke(server_context,
TypeList<>(), std::forward<Args>(
args)...);
489 }
catch (
const Exception& exception) {
490 auto&& results = server_context.
call_context.getResults();
498template <
typename Accessor,
typename Message>
499decltype(
auto)
MaybeGet(Message&& message,
decltype(Accessor::get(message))* enable =
nullptr)
501 return Accessor::get(message);
504template <
typename Accessor>
510template <
class Accessor>
523template <
typename Accessor,
typename... Args>
526 return CustomPassField<Accessor>(std::forward<Args>(
args)...);
529template <
int argc,
typename Accessor,
typename Parent>
534 const Parent&
parent()
const {
return *
this; }
536 template <
typename ServerContext,
typename ArgTypes,
typename... Args>
544 std::forward<Args>(
args)...);
548template <
int argc,
typename Accessor,
typename Parent>
554template <
typename Request>
557template <
typename _Params,
typename _Results>
567template <
typename Client>
570 if (client.m_context.connection) {
571 client.m_context.loop->log() <<
"IPC client destroy " <<
typeid(client).
name();
573 KJ_LOG(INFO,
"IPC interrupted client destroy",
typeid(client).
name());
577template <
typename Server>
580 server.m_context.loop->log() <<
"IPC server destroy " <<
typeid(server).
name();
592template <
typename ProxyClient,
typename GetRequest,
typename... FieldObjs>
608 proxy_client.m_context.loop->logPlain()
610 <<
"} IPC client first request from current thread, constructing waiter";
613 std::optional<ClientInvokeContext> invoke_context;
614 std::exception_ptr exception;
615 std::string kj_exception;
617 const char* disconnected =
nullptr;
618 proxy_client.m_context.loop->sync([&]() {
619 if (!proxy_client.m_context.connection) {
620 const std::unique_lock<std::mutex> lock(thread_context.waiter->m_mutex);
622 disconnected =
"IPC client method called after disconnect.";
623 thread_context.waiter->m_cv.notify_all();
627 auto request = (proxy_client.m_client.*get_request)(
nullptr);
630 invoke_context.emplace(*proxy_client.m_context.connection, thread_context);
631 IterateFields().handleChain(*invoke_context, request, FieldList(),
typename FieldObjs::BuildParams{&fields}...);
632 proxy_client.m_context.loop->logPlain()
633 <<
"{" << thread_context.thread_name <<
"} IPC client send "
634 << TypeName<typename Request::Params>() <<
" " <<
LogEscape(request.toString());
636 proxy_client.m_context.loop->m_task_set->add(request.send().then(
637 [&](::capnp::Response<typename Request::Results>&& response) {
638 proxy_client.m_context.loop->logPlain()
639 <<
"{" << thread_context.thread_name <<
"} IPC client recv "
640 << TypeName<typename Request::Results>() <<
" " << LogEscape(response.toString());
642 IterateFields().handleChain(
643 *invoke_context, response, FieldList(), typename FieldObjs::ReadResults{&fields}...);
645 exception = std::current_exception();
647 const std::unique_lock<std::mutex> lock(thread_context.waiter->m_mutex);
649 thread_context.waiter->m_cv.notify_all();
651 [&](const ::kj::Exception& e) {
652 if (e.getType() == ::kj::Exception::Type::DISCONNECTED) {
653 disconnected =
"IPC client method call interrupted by disconnect.";
655 kj_exception = kj::str(
"kj::Exception: ", e).cStr();
656 proxy_client.m_context.loop->logPlain()
657 <<
"{" << thread_context.thread_name <<
"} IPC client exception " << kj_exception;
659 const std::unique_lock<std::mutex> lock(thread_context.waiter->m_mutex);
661 thread_context.waiter->m_cv.notify_all();
665 std::unique_lock<std::mutex> lock(thread_context.waiter->m_mutex);
666 thread_context.waiter->wait(lock, [&done]() {
return done; });
667 if (exception) std::rethrow_exception(exception);
668 if (!kj_exception.empty()) proxy_client.m_context.loop->raise() << kj_exception;
669 if (disconnected) proxy_client.m_context.loop->raise() << disconnected;
675template <
typename Fn,
typename Ret>
678 if constexpr (std::is_same_v<
decltype(fn()),
void>) {
695template <
typename Server,
typename CallContext,
typename Fn>
696kj::Promise<void>
serverInvoke(Server& server, CallContext& call_context, Fn fn)
698 auto params = call_context.getParams();
699 using Params =
decltype(params);
700 using Results =
typename decltype(call_context.getResults())::Builds;
703 server.m_context.loop->log() <<
"IPC server recv request #" << req <<
" "
704 << TypeName<typename Params::Reads>() <<
" " <<
LogEscape(params.toString());
717 return ReplaceVoid([&]() {
return fn.invoke(server_context, ArgList()); },
718 [&]() {
return kj::Promise<CallContext>(kj::mv(call_context)); })
719 .then([&server, req](CallContext call_context) {
720 server.m_context.loop->log() <<
"IPC server send response #" << req <<
" " << TypeName<Results>()
721 <<
" " <<
LogEscape(call_context.getResults().toString());
723 }
catch (
const std::exception& e) {
724 server.m_context.loop->log() <<
"IPC server unhandled exception: " << e.what();
727 server.m_context.loop->log() <<
"IPC server unhandled exception";
735 template<
typename Interface>
catch(const std::exception &e)
std::unique_ptr< interfaces::Init > init
const CChainParams & Params()
Return the currently selected parameters.
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:
ClientParam< Accessor, Types... > MakeClientParam(Types &&... values)
void MaybeReadField(std::true_type, Args &&... args)
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)
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
decltype(auto) ReadField(TypeList< LocalTypes... >, Args &&... args)
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.
std::string LogEscape(const kj::StringTree &string)
Escape binary string for use in log so it doesn't trigger unicode decode errors in python unit tests.
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)
#define S(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p)
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
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
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
std::unique_ptr< Waiter > waiter
Waiter object used to allow client threads blocked waiting for a server response to execute callbacks...
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.