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 auto list = output.init(value.size());
288 for (
const auto& elem : value) {
294template <
typename LocalType,
typename Input,
typename ReadDest,
typename InitFn,
typename EmplaceFn>
297 return read_dest.update([&](
auto& value) {
298 auto data = input.get();
300 for (
auto item :
data) {
303 return emplace(value, std::forward<
decltype(
args)>(
args)...);
309template <
typename LocalType,
typename Value,
typename Output>
312 output.set(
BuildPrimitive(invoke_context, std::forward<Value>(value),
TypeList<
decltype(output.get())>()));
316template <
typename Accessor,
typename LocalType,
typename ServerContext,
typename Fn,
typename... Args>
318 ->
Require<
typename decltype(Accessor::get(server_context.call_context.getParams()))::Calls>
326 const auto& params = server_context.call_context.getParams();
327 const auto& input = Make<StructField, Accessor>(params);
328 using Interface =
typename Decay<
decltype(input.get())>::Calls;
329 auto param = std::make_unique<ProxyClient<Interface>>(input.get(), server_context.proxy_server.m_context.connection,
false);
330 fn.invoke(server_context, std::forward<Args>(
args)..., *param);
333template <
typename... Args>
338template <
typename... Args>
342template <
typename... Args>
347template <
typename... Args>
352template <
typename LocalType,
typename Value,
typename Output>
360template <
typename LocalTypes,
typename... Args>
366template <
typename Accessor,
typename LocalType,
typename ServerContext,
typename Fn,
typename... Args>
371 std::optional<ArgType> param;
372 const auto& params = server_context.
call_context.getParams();
375 param.emplace(std::forward<
decltype(
args)>(
args)...);
381 if (!param) param.emplace();
383 fn.invoke(server_context, std::forward<Args>(
args)...,
static_cast<LocalType&&
>(*param));
384 auto&& results = server_context.
call_context.getResults();
386 Make<StructField, Accessor>(results), *param);
390template <
typename Accessor,
typename ServerContext,
typename Fn,
typename... Args>
393 const auto& params = server_context.
call_context.getParams();
394 const auto& input = Make<StructField, Accessor>(params);
396 fn.invoke(server_context, std::forward<Args>(
args)...);
397 auto&& results = server_context.
call_context.getResults();
401template <
typename Derived,
size_t N = 0>
404 template <
typename Arg1,
typename Arg2,
typename ParamList,
typename NextFn,
typename... NextFnArgs>
405 void handleChain(Arg1& arg1, Arg2& arg2, ParamList, NextFn&& next_fn, NextFnArgs&&... next_fn_args)
409 next_fn.handleChain(arg1, arg2,
typename S::Second(),
410 std::forward<NextFnArgs>(next_fn_args)...);
413 template <
typename Arg1,
typename Arg2,
typename ParamList>
416 static_cast<Derived*
>(
this)->handleField(arg1, arg2, ParamList());
425 template <
typename Arg1,
typename Arg2,
typename ParamList>
431template <
typename Exception,
typename Accessor>
436 template <
typename Params,
typename ParamList>
447 template <
typename Results,
typename ParamList>
461template <
typename Accessor,
typename... Types>
468 template <
typename Params,
typename ParamList>
471 auto const fun = [&]<
typename... Values>(Values&&...
values) {
474 MaybeBuildField(std::integral_constant<bool, Accessor::in>(), ParamList(), invoke_context,
475 Make<StructField, Accessor>(params), std::forward<Values>(
values)...);
494 template <
typename Results,
typename...
Params>
497 auto const fun = [&]<
typename... Values>(Values&&...
values) {
512template <
typename Accessor,
typename... Types>
515 return {std::forward<Types>(
values)...};
533 [&]() ->
decltype(
auto) {
535 typename decltype(server_context.
call_context.getParams())::Reads
566template <
typename Accessor,
typename Parent>
574 auto&& result = Parent::invoke(server_context,
TypeList<>(), std::forward<Args>(
args)...);
575 auto&& results = server_context.
call_context.getResults();
577 BuildField(
TypeList<
decltype(result)>(), invoke_context, Make<StructField, Accessor>(results),
578 std::forward<
decltype(result)>(result));
582template <
typename Exception,
typename Accessor,
typename Parent>
591 return Parent::invoke(server_context,
TypeList<>(), std::forward<Args>(
args)...);
592 }
catch (
const Exception& exception) {
593 auto&& results = server_context.
call_context.getResults();
601template <
typename Accessor,
typename Message>
602decltype(
auto)
MaybeGet(Message&& message,
decltype(Accessor::get(message))* enable =
nullptr)
604 return Accessor::get(message);
607template <
typename Accessor>
613template <
class Accessor>
626template <
typename Accessor,
typename... Args>
629 return CustomPassField<Accessor>(std::forward<Args>(
args)...);
632template <
int argc,
typename Accessor,
typename Parent>
637 const Parent&
parent()
const {
return *
this; }
639 template <
typename ServerContext,
typename ArgTypes,
typename... Args>
647 std::forward<Args>(
args)...);
651template <
int argc,
typename Accessor,
typename Parent>
657template <
typename Request>
660template <
typename _Params,
typename _Results>
670template <
typename Client>
676template <
typename Server>
691template <
typename ProxyClient,
typename GetRequest,
typename... FieldObjs>
709 <<
"} IPC client first request from current thread, constructing waiter";
712 std::optional<ClientInvokeContext> invoke_context;
713 std::exception_ptr exception;
714 std::string kj_exception;
716 const char* disconnected =
nullptr;
717 proxy_client.m_context.loop->sync([&]() {
718 if (!proxy_client.m_context.connection) {
719 const Lock lock(thread_context.waiter->m_mutex);
721 disconnected =
"IPC client method called after disconnect.";
722 thread_context.waiter->m_cv.notify_all();
726 auto request = (proxy_client.m_client.*get_request)(
nullptr);
729 invoke_context.emplace(*proxy_client.m_context.connection, thread_context);
730 IterateFields().handleChain(*invoke_context, request,
FieldList(),
typename FieldObjs::BuildParams{&fields}...);
732 <<
"{" << thread_context.thread_name <<
"} IPC client send "
733 << TypeName<typename Request::Params>();
735 <<
"send data: " <<
LogEscape(request.toString(), proxy_client.m_context.loop->m_log_opts.max_chars);
737 proxy_client.m_context.loop->m_task_set->add(request.send().then(
738 [&](::capnp::Response<typename Request::Results>&& response) {
739 MP_LOGPLAIN(*proxy_client.m_context.loop, Log::Debug)
740 <<
"{" << thread_context.thread_name <<
"} IPC client recv "
741 << TypeName<typename Request::Results>();
742 MP_LOGPLAIN(*proxy_client.m_context.loop, Log::Trace)
743 <<
"recv data: " << LogEscape(response.toString(), proxy_client.m_context.loop->m_log_opts.max_chars);
745 IterateFields().handleChain(
746 *invoke_context, response, FieldList(), typename FieldObjs::ReadResults{&fields}...);
748 exception = std::current_exception();
750 const Lock lock(thread_context.waiter->m_mutex);
752 thread_context.waiter->m_cv.notify_all();
754 [&](const ::kj::Exception& e) {
755 if (e.getType() == ::kj::Exception::Type::DISCONNECTED) {
756 disconnected =
"IPC client method call interrupted by disconnect.";
758 kj_exception = kj::str(
"kj::Exception: ", e).cStr();
760 <<
"{" << thread_context.thread_name <<
"} IPC client exception " << kj_exception;
762 const Lock lock(thread_context.waiter->m_mutex);
764 thread_context.waiter->m_cv.notify_all();
768 Lock lock(thread_context.waiter->m_mutex);
769 thread_context.waiter->wait(lock, [&done]() {
return done; });
770 if (exception) std::rethrow_exception(exception);
778template <
typename Fn,
typename Ret>
781 if constexpr (std::is_same_v<
decltype(fn()),
void>) {
798template <
typename Server,
typename CallContext,
typename Fn>
799kj::Promise<void>
serverInvoke(Server& server, CallContext& call_context, Fn fn)
801 auto params = call_context.getParams();
802 using Params =
decltype(params);
803 using Results =
typename decltype(call_context.getResults())::Builds;
807 MP_LOG(loop, Log::Debug) <<
"IPC server recv request #" << req <<
" "
808 << TypeName<typename Params::Reads>();
809 MP_LOG(loop, Log::Trace) <<
"request data: "
810 <<
LogEscape(params.toString(), server.m_context.loop->m_log_opts.max_chars);
823 return ReplaceVoid([&]() {
return fn.invoke(server_context, ArgList()); },
824 [&]() {
return kj::Promise<CallContext>(kj::mv(call_context)); })
825 .then([&loop, req](CallContext call_context) {
826 MP_LOG(loop, Log::Debug) <<
"IPC server send response #" << req <<
" " << TypeName<Results>();
827 MP_LOG(loop, Log::Trace) <<
"response data: "
829 }).catch_([&loop, req](::kj::Exception&& e) -> kj::Promise<void> {
833 MP_LOG(loop, Log::Error) <<
"IPC server error request #" << req <<
" " << TypeName<Results>()
834 <<
" " << kj::str(
"kj::Exception: ", e.getDescription()).cStr();
837 }
catch (
const std::exception& e) {
838 MP_LOG(loop, Log::Error) <<
"IPC server unhandled exception: " << e.what();
841 MP_LOG(loop, Log::Error) <<
"IPC server unhandled exception";
849 template<
typename Interface>
catch(const std::exception &e)
std::unique_ptr< interfaces::Init > init
const CChainParams & Params()
Return the currently selected parameters.
Event loop implementation.
LogOptions m_log_opts
Logging options.
void * m_context
External context pointer.
std::unique_lock< std::mutex > m_lock
const Value & get() const
ValueField(Value &&value)
static const PrecomputedData data
Precomputed COutPoint and CCoins values.
std::unique_ptr< ProxyClient< messages::FooInterface > > client
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.
std::string CxxTypeName(const T &)
void clientInvoke(ProxyClient &proxy_client, const GetRequest &get_request, FieldObjs &&... fields)
Entry point called by generated client code that looks like:
void BuildList(TypeList< LocalType >, InvokeContext &invoke_context, Output &&output, Value &&value)
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.
decltype(auto) ReadList(TypeList< LocalType >, InvokeContext &invoke_context, Input &&input, ReadDest &&read_dest, InitFn &&init, EmplaceFn &&emplace)
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.
bool CustomHasField(TypeList< CTransaction >, InvokeContext &invoke_context, const Input &input)
Interpret empty Data fields as null CTransactionRef values.
#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 boxed
Field is a Cap'n Proto pointer type (struct, list, text, data, interface) as opposed to a primitive t...
static const bool optional
Field has a companion has{Name} boolean field in the Cap'n Proto struct.
static const bool requested
Results field has a companion want{Name} boolean field in the Params struct.
static const bool in
Field is present from the Cap'n Proto Params struct (client -> server).
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)
size_t max_chars
Maximum number of characters to use when representing request and response structs as strings.
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.