Squashed 'src/ipc/libmultiprocess/' changes from 35944ffd23fa..27c7e8e5a581

27c7e8e5a581 Merge chaincodelabs/libmultiprocess#172: refactor: fix warnings from clang-tidy-20 and bitcoin-tidy
2fe87d016be4 Merge chaincodelabs/libmultiprocess#173: doc: Fix error string typo
57a65b854664 clang-tidy: Suppress bitcoin-nontrivial-threadlocal error
0d8012f656fe Merge chaincodelabs/libmultiprocess#165: clang-tidy: fix warnings introduced in version 19
3a96cdc18f2d clang-tidy: Fix bugprone-move-forwarding-reference error
c1e8c1a02864 clang-tidy: Fix bugprone-move-forwarding-reference errors
aa19285303ff use ranges transform
a78137ca73b8 make member function const
ca3226ec8ab7 replace custom tuple unpacking code with `std::apply`
949fe85fc91f replace SFINAE trick with `if constexpr`
44ee4b40b89a doc: Fix error string typo

git-subtree-dir: src/ipc/libmultiprocess
git-subtree-split: 27c7e8e5a581b3c41330e758951251ef11807b11
This commit is contained in:
Ryan Ofsky
2025-05-29 13:57:08 -04:00
parent a2f28e4be9
commit 154af1eea1
8 changed files with 107 additions and 81 deletions

View File

@@ -172,7 +172,7 @@ public:
void addClient(std::unique_lock<std::mutex>& lock);
bool removeClient(std::unique_lock<std::mutex>& lock);
//! Check if loop should exit.
bool done(std::unique_lock<std::mutex>& lock);
bool done(std::unique_lock<std::mutex>& lock) const;
Logger log()
{
@@ -249,7 +249,7 @@ struct Waiter
{
const std::unique_lock<std::mutex> lock(m_mutex);
assert(!m_fn);
m_fn = std::move(fn);
m_fn = std::forward<Fn>(fn);
m_cv.notify_all();
}
@@ -333,7 +333,7 @@ public:
// to the EventLoop TaskSet to avoid "Promise callback destroyed itself"
// error in cases where f deletes this Connection object.
m_on_disconnect.add(m_network.onDisconnect().then(
[f = std::move(f), this]() mutable { m_loop.m_task_set->add(kj::evalLater(kj::mv(f))); }));
[f = std::forward<F>(f), this]() mutable { m_loop.m_task_set->add(kj::evalLater(kj::mv(f))); }));
}
EventLoop& m_loop;
@@ -634,7 +634,10 @@ void ListenConnections(EventLoop& loop, int fd, InitImpl& init)
});
}
extern thread_local ThreadContext g_thread_context;
extern thread_local ThreadContext g_thread_context; // NOLINT(bitcoin-nontrivial-threadlocal)
// Silence nonstandard bitcoin tidy error "Variable with non-trivial destructor
// cannot be thread_local" which should not be a problem on modern platforms, and
// could lead to a small memory leak at worst on older ones.
} // namespace mp

View File

@@ -37,20 +37,45 @@ struct StructField
}
Struct& m_struct;
// clang-format off
template<typename A = Accessor> auto get() const -> decltype(A::get(this->m_struct)) { return A::get(this->m_struct); }
template<typename A = Accessor> auto has() const -> std::enable_if_t<A::optional, bool> { return A::getHas(m_struct); }
template<typename A = Accessor> auto has() const -> std::enable_if_t<!A::optional && A::boxed, bool> { return A::has(m_struct); }
template<typename A = Accessor> auto has() const -> std::enable_if_t<!A::optional && !A::boxed, bool> { return true; }
template<typename A = Accessor> auto want() const -> std::enable_if_t<A::requested, bool> { return A::getWant(m_struct); }
template<typename A = Accessor> auto want() const -> std::enable_if_t<!A::requested, bool> { return true; }
template<typename A = Accessor, typename... Args> decltype(auto) set(Args&&... args) const { return A::set(this->m_struct, std::forward<Args>(args)...); }
template<typename A = Accessor, typename... Args> decltype(auto) init(Args&&... args) const { return A::init(this->m_struct, std::forward<Args>(args)...); }
template<typename A = Accessor> auto setHas() const -> std::enable_if_t<A::optional> { return A::setHas(m_struct); }
template<typename A = Accessor> auto setHas() const -> std::enable_if_t<!A::optional> { }
template<typename A = Accessor> auto setWant() const -> std::enable_if_t<A::requested> { return A::setWant(m_struct); }
template<typename A = Accessor> auto setWant() const -> std::enable_if_t<!A::requested> { }
// clang-format on
decltype(auto) get() const { return Accessor::get(this->m_struct); }
bool has() const {
if constexpr (Accessor::optional) {
return Accessor::getHas(m_struct);
} else if constexpr (Accessor::boxed) {
return Accessor::has(m_struct);
} else {
return true;
}
}
bool want() const {
if constexpr (Accessor::requested) {
return Accessor::getWant(m_struct);
} else {
return true;
}
}
template <typename... Args> decltype(auto) set(Args &&...args) const {
return Accessor::set(this->m_struct, std::forward<Args>(args)...);
}
template <typename... Args> decltype(auto) init(Args &&...args) const {
return Accessor::init(this->m_struct, std::forward<Args>(args)...);
}
void setHas() const {
if constexpr (Accessor::optional) {
Accessor::setHas(m_struct);
}
}
void setWant() const {
if constexpr (Accessor::requested) {
Accessor::setWant(m_struct);
}
}
};
@@ -360,34 +385,28 @@ struct ClientException
template <typename Accessor, typename... Types>
struct ClientParam
{
ClientParam(Types&&... values) : m_values(values...) {}
ClientParam(Types&&... values) : m_values{std::forward<Types>(values)...} {}
struct BuildParams : IterateFieldsHelper<BuildParams, sizeof...(Types)>
{
template <typename... Args>
void handleField(Args&&... args)
template <typename Params, typename ParamList>
void handleField(ClientInvokeContext& invoke_context, Params& params, ParamList)
{
callBuild<0>(std::forward<Args>(args)...);
}
auto const fun = [&]<typename... Values>(Values&&... values) {
MaybeBuildField(std::integral_constant<bool, Accessor::in>(), ParamList(), invoke_context,
Make<StructField, Accessor>(params), std::forward<Values>(values)...);
MaybeSetWant(
ParamList(), Priority<1>(), std::forward<Values>(values)..., Make<StructField, Accessor>(params));
};
// TODO Possible optimization to speed up compile time:
// https://stackoverflow.com/a/7858971 Using enable_if below to check
// position when unpacking tuple might be slower than pattern matching
// approach in the stack overflow solution
template <size_t I, typename... Args>
auto callBuild(Args&&... args) -> std::enable_if_t<(I < sizeof...(Types))>
{
callBuild<I + 1>(std::forward<Args>(args)..., std::get<I>(m_client_param->m_values));
}
template <size_t I, typename Params, typename ParamList, typename... Values>
auto callBuild(ClientInvokeContext& invoke_context, Params& params, ParamList, Values&&... values) ->
std::enable_if_t<(I == sizeof...(Types))>
{
MaybeBuildField(std::integral_constant<bool, Accessor::in>(), ParamList(), invoke_context,
Make<StructField, Accessor>(params), std::forward<Values>(values)...);
MaybeSetWant(
ParamList(), Priority<1>(), std::forward<Values>(values)..., Make<StructField, Accessor>(params));
// Note: The m_values tuple just consists of lvalue and rvalue
// references, so calling std::move doesn't change the tuple, it
// just causes std::apply to call the std::get overload that returns
// && instead of &, so rvalue references are preserved and not
// turned into lvalue references. This allows the BuildField call to
// move from the argument if it is an rvalue reference or was passed
// by value.
std::apply(fun, std::move(m_client_param->m_values));
}
BuildParams(ClientParam* client_param) : m_client_param(client_param) {}
@@ -396,24 +415,15 @@ struct ClientParam
struct ReadResults : IterateFieldsHelper<ReadResults, sizeof...(Types)>
{
template <typename... Args>
void handleField(Args&&... args)
template <typename Results, typename... Params>
void handleField(ClientInvokeContext& invoke_context, Results& results, TypeList<Params...>)
{
callRead<0>(std::forward<Args>(args)...);
}
auto const fun = [&]<typename... Values>(Values&&... values) {
MaybeReadField(std::integral_constant<bool, Accessor::out>(), TypeList<Decay<Params>...>(), invoke_context,
Make<StructField, Accessor>(results), ReadDestUpdate(values)...);
};
template <int I, typename... Args>
auto callRead(Args&&... args) -> std::enable_if_t<(I < sizeof...(Types))>
{
callRead<I + 1>(std::forward<Args>(args)..., std::get<I>(m_client_param->m_values));
}
template <int I, typename Results, typename... Params, typename... Values>
auto callRead(ClientInvokeContext& invoke_context, Results& results, TypeList<Params...>, Values&&... values)
-> std::enable_if_t<I == sizeof...(Types)>
{
MaybeReadField(std::integral_constant<bool, Accessor::out>(), TypeList<Decay<Params>...>(), invoke_context,
Make<StructField, Accessor>(results), ReadDestUpdate(values)...);
std::apply(fun, m_client_param->m_values);
}
ReadResults(ClientParam* client_param) : m_client_param(client_param) {}
@@ -574,7 +584,7 @@ void serverDestroy(Server& server)
//!
//! ProxyClient<ClassName>::M0::Result ProxyClient<ClassName>::methodName(M0::Param<0> arg0, M0::Param<1> arg1) {
//! typename M0::Result result;
//! clientInvoke(*this, &InterfaceName::Client::methodNameRequest, MakeClientParam<...>(arg0), MakeClientParam<...>(arg1), MakeClientParam<...>(result));
//! clientInvoke(*this, &InterfaceName::Client::methodNameRequest, MakeClientParam<...>(M0::Fwd<0>(arg0)), MakeClientParam<...>(M0::Fwd<1>(arg1)), MakeClientParam<...>(result));
//! return result;
//! }
//!
@@ -650,19 +660,14 @@ void clientInvoke(ProxyClient& proxy_client, const GetRequest& get_request, Fiel
//! return value with value of `ret()`. This is useful for avoiding code
//! duplication and branching in generic code that forwards calls to functions.
template <typename Fn, typename Ret>
auto ReplaceVoid(Fn&& fn, Ret&& ret) ->
std::enable_if_t<std::is_same_v<void, decltype(fn())>, decltype(ret())>
auto ReplaceVoid(Fn&& fn, Ret&& ret)
{
fn();
return ret();
}
//! Overload of above for non-void `fn()` case.
template <typename Fn, typename Ret>
auto ReplaceVoid(Fn&& fn, Ret&& ret) ->
std::enable_if_t<!std::is_same_v<void, decltype(fn())>, decltype(fn())>
{
return fn();
if constexpr (std::is_same_v<decltype(fn()), void>) {
fn();
return ret();
} else {
return fn();
}
}
extern std::atomic<int> server_reqs;

View File

@@ -181,7 +181,8 @@ struct ProxyServerCustom : public ProxyServerBase<Interface, Impl>
//!
//! Params - TypeList of C++ ClassName::methodName parameter types
//! Result - Return type of ClassName::method
//! Param<N> - helper to access individual parameters by index number.
//! Param<N> - helper to access individual parameter types by index number.
//! Fwd<N> - helper to forward arguments by index number.
//! Fields - helper alias that appends Result type to the Params typelist if
//! it not void.
template <class Fn>
@@ -199,6 +200,16 @@ struct FunctionTraits<_Result (_Class::*const)(_Params...)>
using Param = typename std::tuple_element<N, std::tuple<_Params...>>::type;
using Fields =
std::conditional_t<std::is_same_v<void, Result>, Params, TypeList<_Params..., _Result>>;
//! Enable perfect forwarding for clientInvoke calls. If parameter is a
//! value type or rvalue reference type, pass it as an rvalue-reference to
//! MakeClientParam and BuildField calls so it can be moved from, and if it
//! is an lvalue reference, pass it an lvalue reference so it won't be moved
//! from. This method does the same thing as std::forward except it takes a
//! parameter number instead of a type as a template argument, so generated
//! code calling this can be less repetitive and verbose.
template <size_t N>
static decltype(auto) Fwd(Param<N>& arg) { return static_cast<Param<N>&&>(arg); }
};
//! Traits class for a proxy method, providing the same

View File

@@ -44,7 +44,7 @@ void CustomBuildField(TypeList<std::shared_ptr<Impl>>,
{
if (value) {
using Interface = typename decltype(output.get())::Calls;
output.set(CustomMakeProxyServer<Interface, Impl>(invoke_context, std::move(value)));
output.set(CustomMakeProxyServer<Interface, Impl>(invoke_context, std::forward<Value>(value)));
}
}

View File

@@ -183,7 +183,7 @@ struct AsyncCallable
template <typename Callable>
AsyncCallable<std::remove_reference_t<Callable>> MakeAsyncCallable(Callable&& callable)
{
return std::move(callable);
return std::forward<Callable>(callable);
}
//! Format current thread name as "{exe_name}-{$pid}/{thread_name}-{$tid}".