Skip to content

Commit 106fd58

Browse files
ryanofskySjors
authored andcommitted
multiprocess: Lock CapnpProtocol::m_loop with mutex
This change is intended to fix occasional ipc_tests unit tests and rpc_misc.py functional tests errors that happen on mac os, which sometimes fail with error "terminating due to uncaught exception of type std::__1::system_error: mutex lock failed: Invalid argument" as reported bitcoin-core/libmultiprocess#154 This error could happen when `m_loop.reset();` in `CapnpProtocol::startLoop` executes before the `m_loop->removeClient(lock);` call in the `~CapnpProtocol` destructor returns, causing an failure to reacquire the `EventLoop::m_mutex` inside the `removeClient` method. Fix this error by adding a new mutex to guard access to the `CapnpProtocol::m_loop` variable and making sure `m_loop.reset();` cannot be called until after `m_loop->removeClient(lock);` returns.
1 parent cf56731 commit 106fd58

File tree

1 file changed

+26
-22
lines changed

1 file changed

+26
-22
lines changed

src/ipc/capnp/protocol.cpp

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -41,61 +41,65 @@ class CapnpProtocol : public Protocol
4141
public:
4242
~CapnpProtocol() noexcept(true)
4343
{
44-
if (m_loop) {
45-
std::unique_lock<std::mutex> lock(m_loop->m_mutex);
46-
m_loop->removeClient(lock);
44+
{
45+
LOCK(m_loop_mutex);
46+
if (m_loop) {
47+
std::unique_lock<std::mutex> lock(m_loop->m_mutex);
48+
m_loop->removeClient(lock);
49+
}
4750
}
4851
if (m_loop_thread.joinable()) m_loop_thread.join();
49-
assert(!m_loop);
52+
assert(WITH_LOCK(m_loop_mutex, return !m_loop));
5053
};
51-
std::unique_ptr<interfaces::Init> connect(int fd, const char* exe_name) override
54+
std::unique_ptr<interfaces::Init> connect(int fd, const char* exe_name) override EXCLUSIVE_LOCKS_REQUIRED(!m_loop_mutex)
5255
{
5356
startLoop(exe_name);
54-
return mp::ConnectStream<messages::Init>(*m_loop, fd);
57+
return mp::ConnectStream<messages::Init>(WITH_LOCK(m_loop_mutex, return *m_loop), fd);
5558
}
56-
void listen(int listen_fd, const char* exe_name, interfaces::Init& init) override
59+
void listen(int listen_fd, const char* exe_name, interfaces::Init& init) override EXCLUSIVE_LOCKS_REQUIRED(!m_loop_mutex)
5760
{
5861
startLoop(exe_name);
5962
if (::listen(listen_fd, /*backlog=*/5) != 0) {
6063
throw std::system_error(errno, std::system_category());
6164
}
62-
mp::ListenConnections<messages::Init>(*m_loop, listen_fd, init);
65+
mp::ListenConnections<messages::Init>(WITH_LOCK(m_loop_mutex, return *m_loop), listen_fd, init);
6366
}
64-
void serve(int fd, const char* exe_name, interfaces::Init& init, const std::function<void()>& ready_fn = {}) override
67+
void serve(int fd, const char* exe_name, interfaces::Init& init, const std::function<void()>& ready_fn = {}) override EXCLUSIVE_LOCKS_REQUIRED(!m_loop_mutex)
6568
{
66-
assert(!m_loop);
69+
assert(WITH_LOCK(m_loop_mutex, return !m_loop));
6770
mp::g_thread_context.thread_name = mp::ThreadName(exe_name);
68-
m_loop.emplace(exe_name, &IpcLogFn, &m_context);
71+
auto& loop{WITH_LOCK(m_loop_mutex, m_loop.emplace(exe_name, &IpcLogFn, &m_context); return *m_loop)};
6972
if (ready_fn) ready_fn();
70-
mp::ServeStream<messages::Init>(*m_loop, fd, init);
71-
m_loop->loop();
72-
m_loop.reset();
73+
mp::ServeStream<messages::Init>(loop, fd, init);
74+
loop.loop();
75+
WITH_LOCK(m_loop_mutex, m_loop.reset());
7376
}
7477
void addCleanup(std::type_index type, void* iface, std::function<void()> cleanup) override
7578
{
7679
mp::ProxyTypeRegister::types().at(type)(iface).cleanup_fns.emplace_back(std::move(cleanup));
7780
}
7881
Context& context() override { return m_context; }
79-
void startLoop(const char* exe_name)
82+
void startLoop(const char* exe_name) EXCLUSIVE_LOCKS_REQUIRED(!m_loop_mutex)
8083
{
81-
if (m_loop) return;
84+
if (WITH_LOCK(m_loop_mutex, return m_loop.has_value())) return;
8285
std::promise<void> promise;
8386
m_loop_thread = std::thread([&] {
8487
util::ThreadRename("capnp-loop");
85-
m_loop.emplace(exe_name, &IpcLogFn, &m_context);
88+
auto& loop{WITH_LOCK(m_loop_mutex, m_loop.emplace(exe_name, &IpcLogFn, &m_context); return *m_loop)};
8689
{
87-
std::unique_lock<std::mutex> lock(m_loop->m_mutex);
88-
m_loop->addClient(lock);
90+
std::unique_lock<std::mutex> lock(loop.m_mutex);
91+
loop.addClient(lock);
8992
}
9093
promise.set_value();
91-
m_loop->loop();
92-
m_loop.reset();
94+
loop.loop();
95+
WITH_LOCK(m_loop_mutex, m_loop.reset());
9396
});
9497
promise.get_future().wait();
9598
}
9699
Context m_context;
97100
std::thread m_loop_thread;
98-
std::optional<mp::EventLoop> m_loop;
101+
Mutex m_loop_mutex;
102+
std::optional<mp::EventLoop> m_loop GUARDED_BY(m_loop_mutex);
99103
};
100104
} // namespace
101105

0 commit comments

Comments
 (0)