mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-09-12 13:42:10 +02:00
16bf05dea02 Merge bitcoin-core/libmultiprocess#302: refactor: rename EventLoop::m_num_clients to m_num_refs dd537da9e40 Merge bitcoin-core/libmultiprocess#301: test: recursive async IPC calls and cleanups 400291de000 Merge bitcoin-core/libmultiprocess#299: ci: remove libevent from Core CIs 092be515adf Merge bitcoin-core/libmultiprocess#285: Add ReadList helper 5b617880c51 Merge bitcoin-core/libmultiprocess#283: Add `makePool` method on `ThreadMap` d4998304154 refactor: rename EventLoop::m_num_clients to m_num_refs 6450345c985 type: reserve first when reading std::unordered_set 4d0f8db5f99 proxy: add ReadList helper and dedup map/set/vector read handlers 0e49d911867 Add `makePool` method on `ThreadMap` 5519f7f9485 test: recursive async IPC calls a29ceff40bc ci: remove libevent from Core CIs 8412fcdc659 Merge bitcoin-core/libmultiprocess#295: Mark Waiter m_cv as guarded by m_mutex 1593ee2d18a Merge bitcoin-core/libmultiprocess#294: test: Add passDouble smoke test 9885d7dd33c Merge bitcoin-core/libmultiprocess#286: proxy-client: fix TSan data race in clientDestroy fa35501c4f0 Mark Waiter m_cv as guarded by m_mutex faaedb11f8a test: Add passDouble smoke test 733c64318d1 Merge bitcoin-core/libmultiprocess#292: type-number: fix clang-tidy modernize-use-nullptr 9cc3479ab33 Merge bitcoin-core/libmultiprocess#291: cmake: Add `mp_headers` custom target 201abd9e3a5 Merge bitcoin-core/libmultiprocess#289: cmake: make target_capnp_sources use CURRENT dirs 99820c8aecb Merge bitcoin-core/libmultiprocess#279: doc: Add comments to FIELD_* constants in proxy.h 73b985540c5 Merge bitcoin-core/libmultiprocess#278: doc: Fix and expand design.md e7e91b2e23e Merge bitcoin-core/libmultiprocess#277: Add std::unordered_set support and a helper BuildList to dedup list build handlers 91a951f59ac tidy fix: modernize-use-nullptr 16362f42d01 cmake: Add `mp_headers` custom target 615a94fe3a2 cmake: document ONLY_CAPNP option in target_capnp_sources 90982f75c6b mpgen: iwyu changes required by previous commit 25bb3e67f39 proxy-client: fix TSan data race in clientDestroy 620f297f311 cmake: make target_capnp_sources use CURRENT dirs 9de4b885aa6 test: use camelCase + $Proxy.name for FooStruct fields 011b91793dd type: add std::unordered_set support 20d19b9644e proxy: add BuildList helper and dedup map/set/vector build handlers e863c6cdf61 doc: Add comments to FIELD_* constants in proxy.h 18db0ab9570 doc: Fix and expand design.md 61de6975362 Merge bitcoin-core/libmultiprocess#273: proxy-client: tolerate exceptions from remote destroy during cleanup 9cec9d6ca55 Merge bitcoin-core/libmultiprocess#243: mpgen: support primitive std::optional struct fields 4aaff113745 Merge bitcoin-core/libmultiprocess#238: cmake, ci: updates for recent nixpkgs 2ac55a56b58 Merge bitcoin-core/libmultiprocess#218: Better error and log messages 6de92e1c732 proxy-client: tolerate exceptions from remote destroy during cleanup 90be8354d47 test: regression for ~ProxyClient destroy after peer disconnect 3c69d125a17 Merge bitcoin-core/libmultiprocess#260: event loop: tolerate unexpected exceptions in `post()` callbacks b8a48c65e60 event loop: tolerate unexpected exceptions in `post()` callbacks f787863d2cd Merge bitcoin-core/libmultiprocess#270: doc: Bump version 10 > 11 a22f6029103 doc: Bump version 10 > 11 4eae445d6d8 debug: Add TypeName() function and log statements for Proxy objects being created and destroyed f326c5b1b7b logging: Add better logging on IPC server-side failures 6dbfa56a040 mpgen: support primitive std::optional struct fields 8d1277deb55 mpgen refactor: add AccessorType function db716bbcba7 mpgen refactor: Move field handling code to FieldList class db7acb3ce27 ci: Fix shell.nix compatibility with CMake 4.0 91a7759a9ab cmake: Fix IWYU in nix by adding CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES git-subtree-dir: src/ipc/libmultiprocess git-subtree-split: 16bf05dea02651f75733ff08531181aa774fc5a8
371 lines
12 KiB
C++
371 lines
12 KiB
C++
// Copyright (c) The Bitcoin Core developers
|
|
// Distributed under the MIT software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#ifndef MP_UTIL_H
|
|
#define MP_UTIL_H
|
|
|
|
#include <capnp/schema.h>
|
|
#include <cassert>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <exception>
|
|
#include <functional>
|
|
#include <kj/string-tree.h>
|
|
#include <mutex>
|
|
#include <string>
|
|
#include <tuple>
|
|
#include <typeinfo>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
#include <variant>
|
|
#include <vector>
|
|
|
|
#if __has_include(<cxxabi.h>)
|
|
#include <cxxabi.h>
|
|
#include <memory>
|
|
#endif
|
|
|
|
namespace mp {
|
|
|
|
//! Generic utility functions used by capnp code.
|
|
|
|
//! Type holding a list of types.
|
|
//!
|
|
//! Example:
|
|
//! TypeList<int, bool, void>
|
|
template <typename... Types>
|
|
struct TypeList
|
|
{
|
|
static constexpr size_t size = sizeof...(Types);
|
|
};
|
|
|
|
//! Construct a template class value by deducing template arguments from the
|
|
//! types of constructor arguments, so they don't need to be specified manually.
|
|
//!
|
|
//! Uses of this can go away with class template deduction in C++17
|
|
//! (https://en.cppreference.com/w/cpp/language/class_template_argument_deduction)
|
|
//!
|
|
//! Example:
|
|
//! Make<std::pair>(5, true) // Constructs std::pair<int, bool>(5, true);
|
|
template <template <typename...> class Class, typename... Types, typename... Args>
|
|
Class<Types..., std::remove_reference_t<Args>...> Make(Args&&... args)
|
|
{
|
|
return Class<Types..., std::remove_reference_t<Args>...>{std::forward<Args>(args)...};
|
|
}
|
|
|
|
//! Type helper splitting a TypeList into two halves at position index.
|
|
//!
|
|
//! Example:
|
|
//! is_same<TypeList<int, double>, Split<2, TypeList<int, double, float, bool>>::First>
|
|
//! is_same<TypeList<float, bool>, Split<2, TypeList<int, double, float, bool>>::Second>
|
|
template <std::size_t index, typename List, typename _First = TypeList<>, bool done = index == 0>
|
|
struct Split;
|
|
|
|
//! Specialization of above (base case)
|
|
template <typename _Second, typename _First>
|
|
struct Split<0, _Second, _First, true>
|
|
{
|
|
using First = _First;
|
|
using Second = _Second;
|
|
};
|
|
|
|
//! Specialization of above (recursive case)
|
|
template <std::size_t index, typename Type, typename... _Second, typename... _First>
|
|
struct Split<index, TypeList<Type, _Second...>, TypeList<_First...>, false>
|
|
{
|
|
using _Next = Split<index - 1, TypeList<_Second...>, TypeList<_First..., Type>>;
|
|
using First = typename _Next::First;
|
|
using Second = typename _Next::Second;
|
|
};
|
|
|
|
//! Type helper giving return type of a callable type.
|
|
template <typename Callable>
|
|
using ResultOf = decltype(std::declval<Callable>()());
|
|
|
|
//! Substitutue for std::remove_cvref_t
|
|
template <typename T>
|
|
using RemoveCvRef = std::remove_cv_t<std::remove_reference_t<T>>;
|
|
|
|
//! Type helper abbreviating std::decay.
|
|
template <typename T>
|
|
using Decay = std::decay_t<T>;
|
|
|
|
//! SFINAE helper, see using Require below.
|
|
template <typename SfinaeExpr, typename Result_>
|
|
struct _Require
|
|
{
|
|
using Result = Result_;
|
|
};
|
|
|
|
//! SFINAE helper, basically the same as to C++17's void_t, but allowing types other than void to be returned.
|
|
template <typename SfinaeExpr, typename Result = void>
|
|
using Require = typename _Require<SfinaeExpr, Result>::Result;
|
|
|
|
//! Function parameter type for prioritizing overloaded function calls that
|
|
//! would otherwise be ambiguous.
|
|
//!
|
|
//! Example:
|
|
//! auto foo(Priority<1>) -> std::enable_if<>;
|
|
//! auto foo(Priority<0>) -> void;
|
|
//!
|
|
//! foo(Priority<1>()); // Calls higher priority overload if enabled.
|
|
template <int priority>
|
|
struct Priority : Priority<priority - 1>
|
|
{
|
|
};
|
|
|
|
//! Specialization of above (base case)
|
|
template <>
|
|
struct Priority<0>
|
|
{
|
|
};
|
|
|
|
//! Return capnp type name with filename prefix removed.
|
|
template <typename T>
|
|
const char* TypeName()
|
|
{
|
|
// DisplayName string looks like
|
|
// "interfaces/capnp/common.capnp:ChainNotifications.resendWalletTransactions$Results"
|
|
// This discards the part of the string before the first ':' character.
|
|
// Another alternative would be to use the displayNamePrefixLength field,
|
|
// but this discards everything before the last '.' character, throwing away
|
|
// the object name, which is useful.
|
|
const char* display_name = ::capnp::Schema::from<T>().getProto().getDisplayName().cStr();
|
|
const char* short_name = strchr(display_name, ':');
|
|
return short_name ? short_name + 1 : display_name;
|
|
}
|
|
|
|
//! Convenient wrapper around std::variant<T*, T>
|
|
template <typename T>
|
|
struct PtrOrValue {
|
|
std::variant<T*, T> data;
|
|
|
|
template <typename... Args>
|
|
PtrOrValue(T* ptr, Args&&... args) : data(ptr ? ptr : std::variant<T*, T>{std::in_place_type<T>, std::forward<Args>(args)...}) {}
|
|
|
|
T& operator*() { return data.index() ? std::get<T>(data) : *std::get<T*>(data); }
|
|
T* operator->() { return &**this; }
|
|
T& operator*() const { return data.index() ? std::get<T>(data) : *std::get<T*>(data); }
|
|
T* operator->() const { return &**this; }
|
|
};
|
|
|
|
// Annotated mutex and lock class (https://clang.llvm.org/docs/ThreadSafetyAnalysis.html)
|
|
#if defined(__clang__) && (!defined(SWIG))
|
|
#define MP_TSA(x) __attribute__((x))
|
|
#else
|
|
#define MP_TSA(x) // no-op
|
|
#endif
|
|
|
|
#define MP_CAPABILITY(x) MP_TSA(capability(x))
|
|
#define MP_SCOPED_CAPABILITY MP_TSA(scoped_lockable)
|
|
#define MP_REQUIRES(x) MP_TSA(requires_capability(x))
|
|
#define MP_ACQUIRE(...) MP_TSA(acquire_capability(__VA_ARGS__))
|
|
#define MP_RELEASE(...) MP_TSA(release_capability(__VA_ARGS__))
|
|
#define MP_ASSERT_CAPABILITY(x) MP_TSA(assert_capability(x))
|
|
#define MP_GUARDED_BY(x) MP_TSA(guarded_by(x))
|
|
#define MP_NO_TSA MP_TSA(no_thread_safety_analysis)
|
|
|
|
class MP_CAPABILITY("mutex") Mutex {
|
|
public:
|
|
void lock() MP_ACQUIRE() { m_mutex.lock(); }
|
|
void unlock() MP_RELEASE() { m_mutex.unlock(); }
|
|
|
|
std::mutex m_mutex;
|
|
};
|
|
|
|
class MP_SCOPED_CAPABILITY Lock {
|
|
public:
|
|
explicit Lock(Mutex& m) MP_ACQUIRE(m) : m_lock(m.m_mutex) {}
|
|
~Lock() MP_RELEASE() = default;
|
|
void unlock() MP_RELEASE() { m_lock.unlock(); }
|
|
void lock() MP_ACQUIRE() { m_lock.lock(); }
|
|
void assert_locked(Mutex& mutex) MP_ASSERT_CAPABILITY() MP_ASSERT_CAPABILITY(mutex)
|
|
{
|
|
assert(m_lock.mutex() == &mutex.m_mutex);
|
|
assert(m_lock);
|
|
}
|
|
|
|
std::unique_lock<std::mutex> m_lock;
|
|
};
|
|
|
|
template<typename T>
|
|
struct GuardedRef
|
|
{
|
|
Mutex& mutex;
|
|
T& ref MP_GUARDED_BY(mutex);
|
|
};
|
|
|
|
// CTAD for Clang 16: GuardedRef{mutex, x} -> GuardedRef<decltype(x)>
|
|
template <class U>
|
|
GuardedRef(Mutex&, U&) -> GuardedRef<U>;
|
|
|
|
//! Analog to std::lock_guard that unlocks instead of locks.
|
|
template <typename Lock>
|
|
struct UnlockGuard
|
|
{
|
|
UnlockGuard(Lock& lock) : m_lock(lock) { m_lock.unlock(); }
|
|
~UnlockGuard() { m_lock.lock(); }
|
|
Lock& m_lock;
|
|
};
|
|
|
|
template <typename Lock, typename Callback>
|
|
void Unlock(Lock& lock, Callback&& callback)
|
|
{
|
|
const UnlockGuard<Lock> unlock(lock);
|
|
callback();
|
|
}
|
|
|
|
//! Invoke a function and run a follow-up action before returning the original
|
|
//! result.
|
|
//!
|
|
//! This can be used similarly to KJ_DEFER to run cleanup code, but works better
|
|
//! if the cleanup function can throw because it avoids clang bug
|
|
//! https://github.com/llvm/llvm-project/issues/12658 which skips calling
|
|
//! destructors in that case and can lead to memory leaks. Also, if both
|
|
//! functions throw, this lets one exception take precedence instead of
|
|
//! terminating due to having two active exceptions.
|
|
template <typename Fn, typename After>
|
|
decltype(auto) TryFinally(Fn&& fn, After&& after)
|
|
{
|
|
bool success{false};
|
|
using R = std::invoke_result_t<Fn>;
|
|
try {
|
|
if constexpr (std::is_void_v<R>) {
|
|
std::forward<Fn>(fn)();
|
|
success = true;
|
|
std::forward<After>(after)();
|
|
return;
|
|
} else {
|
|
decltype(auto) result = std::forward<Fn>(fn)();
|
|
success = true;
|
|
std::forward<After>(after)();
|
|
return result;
|
|
}
|
|
} catch (...) {
|
|
if (!success) std::forward<After>(after)();
|
|
throw;
|
|
}
|
|
}
|
|
|
|
//! Format current thread name as "{exe_name}-{$pid}/{thread_name}-{$tid}".
|
|
std::string ThreadName(const char* exe_name);
|
|
|
|
//! Escape binary string for use in log so it doesn't trigger unicode decode
|
|
//! errors in python unit tests.
|
|
std::string LogEscape(const kj::StringTree& string, size_t max_size);
|
|
|
|
//! Callback type used by SpawnProcess below.
|
|
using FdToArgsFn = std::function<std::vector<std::string>(int fd)>;
|
|
|
|
//! Spawn a new process that communicates with the current process over a socket
|
|
//! pair. Returns pid through an output argument, and file descriptor for the
|
|
//! local side of the socket.
|
|
//! The fd_to_args callback is invoked in the parent process before fork().
|
|
//! It must not rely on child pid/state, and must return the command line
|
|
//! arguments that should be used to execute the process. Embed the remote file
|
|
//! descriptor number in whatever format the child process expects.
|
|
int SpawnProcess(int& pid, FdToArgsFn&& fd_to_args);
|
|
|
|
//! Call execvp with vector args.
|
|
//! Not safe to call in a post-fork child of a multi-threaded process.
|
|
//! Currently only used by mpgen at build time.
|
|
void ExecProcess(const std::vector<std::string>& args);
|
|
|
|
//! Wait for a process to exit and return its exit code.
|
|
int WaitProcess(int pid);
|
|
|
|
inline char* CharCast(char* c) { return c; }
|
|
inline char* CharCast(unsigned char* c) { return (char*)c; }
|
|
inline const char* CharCast(const char* c) { return c; }
|
|
inline const char* CharCast(const unsigned char* c) { return (const char*)c; }
|
|
|
|
#if __has_include(<cxxabi.h>) // GCC & Clang ─ use <cxxabi.h> to demangle
|
|
inline std::string _demangle(const char* m)
|
|
{
|
|
int status = 0;
|
|
std::unique_ptr<char, void(*)(void*)> p{
|
|
abi::__cxa_demangle(m, /*output_buffer=*/nullptr, /*length=*/nullptr, &status), std::free};
|
|
return (status == 0 && p) ? p.get() : m; // fall back on mangled if needed
|
|
}
|
|
#else // MSVC or other ─ no demangling available
|
|
inline std::string _demangle(const char* m) { return m; }
|
|
#endif
|
|
|
|
template<class T>
|
|
std::string CxxTypeName(const T& /*unused*/)
|
|
{
|
|
#ifdef __cpp_rtti
|
|
return _demangle(typeid(std::decay_t<T>).name());
|
|
#else
|
|
return "<type information unavailable without rtti>";
|
|
#endif
|
|
}
|
|
|
|
//! Exception thrown from code executing an IPC call that is interrupted.
|
|
struct InterruptException final : std::exception {
|
|
explicit InterruptException(std::string message) : m_message(std::move(message)) {}
|
|
const char* what() const noexcept override { return m_message.c_str(); }
|
|
std::string m_message;
|
|
};
|
|
|
|
class CancelProbe;
|
|
|
|
//! Helper class that detects when a promise is canceled. Used to detect
|
|
//! canceled requests and prevent potential crashes on unclean disconnects.
|
|
//!
|
|
//! In the future, this could also be used to support a way for wrapped C++
|
|
//! methods to detect cancellation (like approach #4 in
|
|
//! https://github.com/bitcoin/bitcoin/issues/33575).
|
|
class CancelMonitor
|
|
{
|
|
public:
|
|
inline ~CancelMonitor();
|
|
inline void promiseDestroyed(CancelProbe& probe);
|
|
|
|
bool m_canceled{false};
|
|
std::function<void()> m_on_cancel;
|
|
CancelProbe* m_probe{nullptr};
|
|
};
|
|
|
|
//! Helper object to attach to a promise and update a CancelMonitor.
|
|
class CancelProbe
|
|
{
|
|
public:
|
|
CancelProbe(CancelMonitor& monitor) : m_monitor(&monitor)
|
|
{
|
|
assert(!monitor.m_probe);
|
|
monitor.m_probe = this;
|
|
}
|
|
~CancelProbe()
|
|
{
|
|
if (m_monitor) m_monitor->promiseDestroyed(*this);
|
|
}
|
|
CancelMonitor* m_monitor;
|
|
};
|
|
|
|
CancelMonitor::~CancelMonitor()
|
|
{
|
|
if (m_probe) {
|
|
assert(m_probe->m_monitor == this);
|
|
m_probe->m_monitor = nullptr;
|
|
m_probe = nullptr;
|
|
}
|
|
}
|
|
|
|
void CancelMonitor::promiseDestroyed(CancelProbe& probe)
|
|
{
|
|
// If promise is being destroyed, assume the promise has been canceled. In
|
|
// theory this method could be called when a promise was fulfilled or
|
|
// rejected rather than canceled, but it's safe to assume that's not the
|
|
// case because the CancelMonitor class is meant to be used inside code
|
|
// fulfilling or rejecting the promise and destroyed before doing so.
|
|
assert(m_probe == &probe);
|
|
m_canceled = true;
|
|
if (m_on_cancel) m_on_cancel();
|
|
m_probe = nullptr;
|
|
}
|
|
} // namespace mp
|
|
|
|
#endif // MP_UTIL_H
|