You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
bug: fix ProxyClient<Thread> deadlock if disconnected as IPC call is returning
This bug is currently causing mptest "disconnecting and blocking" test to
occasionally hang as reported by maflcko in
bitcoin/bitcoin#33244.
The bug was actually first reported by Sjors in
Sjors/bitcoin#90 (comment) and there are
more details about it in
#189.
The bug is caused by the "disconnecting and blocking" test triggering a
disconnect right before a server IPC call returns. This results in a race
between the IPC server thread and the onDisconnect handler in the event loop
thread both trying to destroy the server's request_threads ProxyClient<Thread>
object when the IPC call is done.
There was a lack of synchronization in this case, fixed here by adding
loop->sync() a few places. Specifically the fixes were to:
- Always call SetThread on the event loop thread using the loop->sync() method,
to prevent a race between the ProxyClient<Thread> creation code and the
connection shutdown code if there was an ill-timed disconnect.
- Similarly in ~ProxyClient<Thread> and thread-context.h PassField(), use
loop->sync() when destroying the thread object, in case a disconnect happens
at that time.
A few other changes were made in this commit to make the resulting code safer
and simpler, even though they are not technically necessary for the fix:
- In thread-context.h PassField(), Waiter::m_mutex is now unlocked while
destroying ProxyClient<Thread> just to respect EventLoop::m_mutex and
Waiter::m_mutex lock order and never lock the Waiter first. This is just for
consistency. There is no actually possibility for a deadlock here due to the
new sync() call.
- This adds asserts to make sure functions expected to run on the event
loop thread are only called on that thread.
- This inlines the ProxyClient<Thread>::setDisconnectCallback function, just
because it was a short function only called in a single place.
0 commit comments