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 {
66 return Accessor::init(this->m_struct, std::forward<Args>(
args)...);
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();
233 throw std::runtime_error(std::string(
CharCast(data.begin()), data.size()));
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();
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();
364template <
typename Accessor,
typename ServerContext,
typename Fn,
typename... Args>
367 const auto& params = server_context.
call_context.getParams();
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,
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();
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>
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);
710 <<
"{" << thread_context.
thread_name <<
"} IPC client send "
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>) {
767extern std::atomic<int> server_reqs;
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 <<
" "
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) {
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)
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)
void clientInvoke(ProxyClient &proxy_client, const GetRequest &get_request, FieldObjs &&... fields)
const char * TypeName()
Return capnp type name with filename prefix removed.
bool CustomHasField(TypeList< LocalTypes... >, InvokeContext &invoke_context, const Input &input)
ClientParam< Accessor, Types... > MakeClientParam(Types &&... values)
void MaybeReadField(std::true_type, Args &&... args)
decltype(auto) TryFinally(Fn &&fn, After &&after)
kj::Promise< void > serverInvoke(Server &server, CallContext &call_context, Fn fn)
std::decay_t< T > Decay
Type helper abbreviating std::decay.
void BuildField(TypeList< LocalTypes... >, Context &context, Output &&output, Values &&... values)
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...
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)
LocalType BuildPrimitive(InvokeContext &invoke_context, const Value &value, TypeList< LocalType >, typename std::enable_if< std::is_enum< Value >::value >::type *enable=nullptr)
std::atomic< int > server_reqs
void ThrowField(TypeList< LocalType >, InvokeContext &invoke_context, Input &&input)
void MaybeSetWant(TypeList< LocalType * >, Priority< 1 >, const Value &value, Output &&output)
void serverDestroy(Server &server)
Class< Types..., std::remove_reference_t< Args >... > Make(Args &&... args)
std::remove_cv_t< std::remove_reference_t< T > > RemoveCvRef
Substitutue for std::remove_cvref_t.
ThreadContext g_thread_context
decltype(auto) ReadField(TypeList< LocalTypes... >, InvokeContext &invoke_context, Input &&input, Args &&... args)
decltype(auto) MaybeGet(Message &&message, decltype(Accessor::get(message)) *enable=nullptr)
ServerInvokeContext< ProxyServer< Interface >, ::capnp::CallContext< Params, Results > > ServerContext
auto ReplaceVoid(Fn &&fn, Ret &&ret)
void CustomBuildField(TypeList< LocalType >, Priority< 1 >, InvokeContext &invoke_context, Value &&value, Output &&output)
ServerField< argc, Accessor, Parent > MakeServerField(Parent parent)
std::string LogEscape(const kj::StringTree &string, size_t max_size)
#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)
Context data associated with proxy client and server classes.
Mapping from local c++ type to capnp type and traits (specializations are generated by proxy-codegen....
ProxyTypeRegister(TypeList< Interface >)
std::map< std::type_index, ProxyContext &(*)(void *)> Types
decltype(auto) construct(Args &&... args)
ReadDestEmplace(TypeList< LocalType >, EmplaceFn emplace_fn)
decltype(auto) update(UpdateFn &&update_fn)
Value & update(UpdateFn &&update_fn)
Simple case. If ReadField works by calling update() just forward arguments to update_fn.
Value & construct(Args &&... args)
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
decltype(auto) set(Args &&...args) const
decltype(auto) init(Args &&...args) const
decltype(auto) get() const
std::unique_ptr< Waiter > waiter
std::string thread_name
Identifying string for debug.
Generic utility functions used by capnp code.