Skip to content

Fix mptest failures in bitcoin CI #186

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Jul 1, 2025
Merged

Conversation

ryanofsky
Copy link
Collaborator

@ryanofsky ryanofsky commented Jun 27, 2025

Recently merged PR #160 expanded unit tests to cover various unclean disconnection scenarios, but the new unit tests cause failures in bitcoin CI, despite passing in local CI (which doesn't test as many sanitizers and platforms). Some of the errors are just test bugs, but others are real library bugs and race conditions.

The bugs were reported in two threads starting Sjors/bitcoin#90 (comment) and bitcoin/bitcoin#32345 (comment), and they are described in detail in individual commit messages in this PR. The changes here fix all the known bugs and add new CI jobs and tests to detect them and catch regressions.

@DrahtBot
Copy link

DrahtBot commented Jun 27, 2025

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Reviews

See the guideline for information on the review process.

Type Reviewers
ACK Sjors

If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.

@Sjors
Copy link
Member

Sjors commented Jun 27, 2025

You'll need this to please the Bitcoin Core linter: 8ac8b4c

Fix race condition caught by TSAN between TestSetup constructor and std::thread
launched by the constructor, which could lead to the constructor setting member
variables to default values after the std::thread set them, which would not be
the intended order and could lead to bugs.

This change fixes all TSAN errors from the test except one, which will be fixed
in a subsequent commit in this PR.

The TSAN errors looked like the following (this is just the first error, there
were different ones as well):

[ TEST ] test.cpp:108: Call FooInterface methods
==================
WARNING: ThreadSanitizer: data race (pid=1970628)
  Write of size 8 at 0x7ffcf7ba4c30 by thread T1:
    #0 mp::test::TestSetup::TestSetup(bool)::{lambda()bitcoin-core#1}::operator()() const::{lambda(mp::Connection&)bitcoin-core#1}::operator()(mp::Connection&) const test/mp/test/test.cpp:71 (mptest+0x131c3d)
    bitcoin-core#1 capnp::Capability::Client std::__invoke_impl<capnp::Capability::Client, mp::test::TestSetup::TestSetup(bool)::{lambda()bitcoin-core#1}::operator()() const::{lambda(mp::Connection&)bitcoin-core#1}&, mp::Connection&>(std::__invoke_other, mp::test::TestSetup::TestSetup(bool)::{lambda()bitcoin-core#1}::operator()() const::{lambda(mp::Connection&)bitcoin-core#1}&, mp::Connection&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/invoke.h:61 (mptest+0x131a25)
    bitcoin-core#2 std::enable_if<is_invocable_r_v<capnp::Capability::Client, mp::test::TestSetup::TestSetup(bool)::{lambda()bitcoin-core#1}::operator()() const::{lambda(mp::Connection&)bitcoin-core#1}&, mp::Connection&>, capnp::Capability::Client>::type std::__invoke_r<capnp::Capability::Client, mp::test::TestSetup::TestSetup(bool)::{lambda()bitcoin-core#1}::operator()() const::{lambda(mp::Connection&)bitcoin-core#1}&, mp::Connection&>(mp::test::TestSetup::TestSetup(bool)::{lambda()bitcoin-core#1}::operator()() const::{lambda(mp::Connection&)bitcoin-core#1}&, mp::Connection&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/invoke.h:114 (mptest+0x131a25)
    bitcoin-core#3 std::_Function_handler<capnp::Capability::Client (mp::Connection&), mp::test::TestSetup::TestSetup(bool)::{lambda()bitcoin-core#1}::operator()() const::{lambda(mp::Connection&)bitcoin-core#1}>::_M_invoke(std::_Any_data const&, mp::Connection&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_function.h:290 (mptest+0x131a25)
    bitcoin-core#4 mp::Connection::Connection(mp::EventLoop&, kj::Own<kj::AsyncIoStream, decltype(nullptr)>&&, std::function<capnp::Capability::Client (mp::Connection&)> const&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_function.h:591 (mptest+0x13167f)
    bitcoin-core#5 mp::test::TestSetup::TestSetup(bool)::{lambda()bitcoin-core#1}::operator()() const /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/unique_ptr.h:1077 (mptest+0x13054c)
    bitcoin-core#6 void std::__invoke_impl<void, mp::test::TestSetup::TestSetup(bool)::{lambda()bitcoin-core#1}>(std::__invoke_other, mp::test::TestSetup::TestSetup(bool)::{lambda()bitcoin-core#1}&&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/invoke.h:61 (mptest+0x1303f9)
    bitcoin-core#7 std::__invoke_result<mp::test::TestSetup::TestSetup(bool)::{lambda()bitcoin-core#1}>::type std::__invoke<mp::test::TestSetup::TestSetup(bool)::{lambda()bitcoin-core#1}>(mp::test::TestSetup::TestSetup(bool)::{lambda()bitcoin-core#1}&&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/invoke.h:96 (mptest+0x1303f9)
    bitcoin-core#8 void std::thread::_Invoker<std::tuple<mp::test::TestSetup::TestSetup(bool)::{lambda()bitcoin-core#1}> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_thread.h:301 (mptest+0x1303f9)
    bitcoin-core#9 std::thread::_Invoker<std::tuple<mp::test::TestSetup::TestSetup(bool)::{lambda()bitcoin-core#1}> >::operator()() /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_thread.h:308 (mptest+0x1303f9)
    bitcoin-core#10 std::thread::_State_impl<std::thread::_Invoker<std::tuple<mp::test::TestSetup::TestSetup(bool)::{lambda()bitcoin-core#1}> > >::_M_run() /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_thread.h:253 (mptest+0x1303f9)
    bitcoin-core#11 execute_native_thread_routine ??:? (libstdc++.so.6+0xed063)

  Previous write of size 8 at 0x7ffcf7ba4c30 by main thread:
    #0 __tsan_memset ??:? (mptest+0x8811f)
    bitcoin-core#1 mp::test::TestSetup::TestSetup(bool) test/mp/test/test.cpp:57 (mptest+0x12c3b4)
    bitcoin-core#2 mp::test::TestCase108::run() test/mp/test/test.cpp:110 (mptest+0x128160)
    bitcoin-core#3 kj::Maybe<kj::Exception> kj::runCatchingExceptions<kj::TestRunner::run()::{lambda()bitcoin-core#1}>(kj::TestRunner::run()::{lambda()bitcoin-core#1}&&) ??:? (libkj-test.so.1.1.0+0x7290) (BuildId: 50ddf81234cd06daf5f2d3f11713ed193ade4eb7)

  Location is stack of main thread.

  Thread T1 (tid=1970630, running) created by main thread at:
    #0 pthread_create ??:? (mptest+0x9a2a5)
    bitcoin-core#1 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State> >, void (*)()) ??:? (libstdc++.so.6+0xed138)
    bitcoin-core#2 mp::test::TestCase108::run() test/mp/test/test.cpp:110 (mptest+0x128160)
    bitcoin-core#3 kj::Maybe<kj::Exception> kj::runCatchingExceptions<kj::TestRunner::run()::{lambda()bitcoin-core#1}>(kj::TestRunner::run()::{lambda()bitcoin-core#1}&&) ??:? (libkj-test.so.1.1.0+0x7290) (BuildId: 50ddf81234cd06daf5f2d3f11713ed193ade4eb7)

SUMMARY: ThreadSanitizer: data race test/mp/test/test.cpp:71 in mp::test::TestSetup::TestSetup(bool)::{lambda()bitcoin-core#1}::operator()() const::{lambda(mp::Connection&)bitcoin-core#1}::operator()(mp::Connection&) const
Rename `ProxyClient<Thread>` `m_cleanup_it` member to `m_disconnect_cb`.

There is no change in behavor in this commit. The change is just being made so
a bugfix in an upcoming commit will be more obvious.

The `m_cleanup_it` name was a bad name because it didn't make it clear that
this cleanup function only runs on sudden disconnects, not clean shutdowns. It
was also confusing because this is a member of ProxyClient object, which has
its own cleanup functions, but this doesn't actually refer to one of those
functions, it refers to a cleanup function in an entirely different object (the
Connection object).
Rename `ProxyClient` constructor `cleanup_it` local variable to `disconnect_cb`
to make a bugfix in an upcoming commit more obvious. Reasons for renaming this
variable are the same as the ones stated for renaming a similar variable in the
previous commit.
@ryanofsky
Copy link
Collaborator Author

Rebased 1ff5ad1 -> 258b83c (pr/tsan.2 -> pr/tsan.3, compare) splitting up the second commit, simplifying it and making it broader so I think it will cover the other data race failure reported Sjors/bitcoin#90 (comment) https://cirrus-ci.com/task/4902764083412992?logs=ci#L3539, but I haven't looked into the other failures reported there yet.

This fixes a TSAN data race in the "disconnecting and blocking during the call"
unit test. The error output is below and problem it shows is that the
ProxyClientBase `m_context.connection->removeSyncCleanup(disconnect_cb);` call
is dereferencing `Connection::m_loop.m_loop` pointer variable shortly before
the ~Connection destructor sets the pointer to null in another thread, without
any synchronization in between.

This race could potentially cause segfaults if the timing was different and the
onDisconnect call happened at the same time as the removeSyncCleanup call.

Fix the error by moving the removeSyncCleanup call into a loop->sync() block so
it runs on the event loop thread instead of running asynchronously.

Moving this call fixes the TSAN error shown below but it causes 3 new TSAN
errors in the same unit test, which will be fixed in a seperate commit. The
TSAN error below error shows the ProxyClientBase object not using proper
synchronization when accessing a Connection object field before the Connection
object is destroyed. But this fix here causes Connection destruction to
delayed, so the new errors show the opposite problem of code called from the
Connection destructor not using proper synchronization before accessing
ProxyClientBase fields.

To explain the error output below in more detail: The test case that triggers
this data race has 3 threads. There is the main client thread calling
foo->callFnAsync() which is idle during this time. There is the corresponding
server thread T11 in the stack trace below which is executing the callFnAsync()
function, and calling setup.client_disconnect() to check for correct behavior
when there is a disconnect during the IPC call. Finally there is the event
loop thread T10 in the stack trace below which is actually deleting the server
Connection object in response to the client_disconnect() call (which deletes
the client Connection object).

During the callFnAsync() call, the server thread T11 responsible for executing
that call creates a ProxyClient<mp::Thread> object and inserts it into the
g_thread_context.request_threads std::map in the type-context.h CustomPassField
function, in case the server needs to make an IPC call to the client (which it
does not in this test). Before the CustomPassField funcion returns it deletes
the entry in the request_threads map, deleting the ProxyClient<Thread> object,
and leading to the ProxyClientBase<Thread>
`m_context.connection->removeSyncCleanup(disconnect_cb);` call referenced in the
beginning which accesses the m_loop pointer variable without a lock.

Error looks like:

WARNING: ThreadSanitizer: data race (pid=2025645)
  Write of size 8 at 0x726000012c00 by thread T10:
    #0 mp::EventLoopRef::reset(bool) src/mp/proxy.cpp:61 (mptest+0x2213bf)
    bitcoin-core#1 mp::Connection::~Connection() include/mp/proxy.h:58 (mptest+0x221a9b)
    bitcoin-core#2 kj::_::TransformPromiseNode<kj::_::Void, kj::_::Void, mp::test::TestSetup::TestSetup(bool)::{lambda()bitcoin-core#1}::operator()() const::{lambda()bitcoin-core#2}, kj::_::PropagateException>::getImpl(kj::_::ExceptionOrValue&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/unique_ptr.h:93 (mptest+0x133817)
    bitcoin-core#3 kj::_::TransformPromiseNodeBase::get(kj::_::ExceptionOrValue&) ??:? (libkj-async.so.1.1.0+0x3da3d) (BuildId: 21ff3d5ab929b5e72be4dbbab1a9223f705af6e4)
    bitcoin-core#4 mp::EventLoop::loop() src/mp/proxy.cpp:229 (mptest+0x222f60)
    bitcoin-core#5 mp::test::TestSetup::TestSetup(bool)::{lambda()bitcoin-core#1}::operator()() const test/mp/test/test.cpp:92 (mptest+0x130d23)
    bitcoin-core#6 void std::__invoke_impl<void, mp::test::TestSetup::TestSetup(bool)::{lambda()bitcoin-core#1}>(std::__invoke_other, mp::test::TestSetup::TestSetup(bool)::{lambda()bitcoin-core#1}&&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/invoke.h:61 (mptest+0x130789)
    bitcoin-core#7 std::__invoke_result<mp::test::TestSetup::TestSetup(bool)::{lambda()bitcoin-core#1}>::type std::__invoke<mp::test::TestSetup::TestSetup(bool)::{lambda()bitcoin-core#1}>(mp::test::TestSetup::TestSetup(bool)::{lambda()bitcoin-core#1}&&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/invoke.h:96 (mptest+0x130789)
    bitcoin-core#8 void std::thread::_Invoker<std::tuple<mp::test::TestSetup::TestSetup(bool)::{lambda()bitcoin-core#1}> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_thread.h:301 (mptest+0x130789)
    bitcoin-core#9 std::thread::_Invoker<std::tuple<mp::test::TestSetup::TestSetup(bool)::{lambda()bitcoin-core#1}> >::operator()() /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_thread.h:308 (mptest+0x130789)
    bitcoin-core#10 std::thread::_State_impl<std::thread::_Invoker<std::tuple<mp::test::TestSetup::TestSetup(bool)::{lambda()bitcoin-core#1}> > >::_M_run() /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_thread.h:253 (mptest+0x130789)
    bitcoin-core#11 execute_native_thread_routine ??:? (libstdc++.so.6+0xed063)

  Previous read of size 8 at 0x726000012c00 by thread T11 (mutexes: write M0):
    #0 mp::Connection::removeSyncCleanup(std::_List_iterator<std::function<void ()> >) include/mp/proxy.h:60 (mptest+0x221dbf)
    bitcoin-core#1 std::_Function_handler<void (), mp::ProxyClientBase<mp::Thread, capnp::Void>::ProxyClientBase(mp::Thread::Client, mp::Connection*, bool)::{lambda()bitcoin-core#2}>::_M_invoke(std::_Any_data const&) include/mp/proxy-io.h:419 (mptest+0x228814)
    bitcoin-core#2 mp::CleanupRun(std::__cxx11::list<std::function<void ()>, std::allocator<std::function<void ()> > >&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_function.h:591 (mptest+0x1ca65b)
    bitcoin-core#3 mp::ProxyClientBase<mp::Thread, capnp::Void>::~ProxyClientBase() include/mp/proxy-io.h:446 (mptest+0x227790)
    bitcoin-core#4 mp::ProxyClient<mp::Thread>::~ProxyClient() src/mp/proxy.cpp:339 (mptest+0x223fbd)
    bitcoin-core#5 std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> >::~pair() /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/stl_iterator.h:3013 (mptest+0x1cad8a)
    bitcoin-core#6 void std::destroy_at<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >(std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> >*) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/stl_construct.h:88 (mptest+0x1cad8a)
    bitcoin-core#7 void std::allocator_traits<std::allocator<std::_Rb_tree_node<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > > > >::destroy<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >(std::allocator<std::_Rb_tree_node<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > > >&, std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> >*) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/alloc_traits.h:599 (mptest+0x1cad8a)
    bitcoin-core#8 std::_Rb_tree<mp::Connection*, std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> >, std::_Select1st<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >, std::less<mp::Connection*>, std::allocator<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > > >::_M_destroy_node(std::_Rb_tree_node<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >*) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/stl_tree.h:621 (mptest+0x1cad8a)
    bitcoin-core#9 std::_Rb_tree<mp::Connection*, std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> >, std::_Select1st<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >, std::less<mp::Connection*>, std::allocator<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > > >::_M_drop_node(std::_Rb_tree_node<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >*) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/stl_tree.h:629 (mptest+0x1cad8a)
    bitcoin-core#10 std::_Rb_tree<mp::Connection*, std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> >, std::_Select1st<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >, std::less<mp::Connection*>, std::allocator<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > > >::_M_erase(std::_Rb_tree_node<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >*) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/stl_tree.h:1934 (mptest+0x1cad8a)
    bitcoin-core#11 std::_Rb_tree<mp::Connection*, std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> >, std::_Select1st<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >, std::less<mp::Connection*>, std::allocator<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > > >::_M_erase_aux(std::_Rb_tree_const_iterator<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >, std::_Rb_tree_const_iterator<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/stl_tree.h:1251 (mptest+0x1cace1)
    bitcoin-core#12 _ZZZN2mp9PassFieldINS_8AccessorINS_10foo_fields7ContextELi17EEENS_19ServerInvokeContextINS_11ProxyServerINS_4test8messages12FooInterfaceEEEN5capnp11CallContextINS9_17CallFnAsyncParamsENS9_18CallFnAsyncResultsEEEEENS_10ServerCallEJNS_8TypeListIJEEEEEENSt9enable_ifIXsr3std7is_sameIDTclsrT_3getcldtdtfL1p1_12call_context9getParamsEEENS_7Context6ReaderEEE5valueEN2kj7PromiseINT0_11CallContextEEEE4typeENS_8PriorityILi1EEESJ_RSR_RKT1_DpOT2_ENUlvE_clEvENKUlvE0_clEv /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/stl_tree.h:2519 (mptest+0x1f9b7e)
    bitcoin-core#13 _ZN2kj1_8DeferredIZZN2mp9PassFieldINS2_8AccessorINS2_10foo_fields7ContextELi17EEENS2_19ServerInvokeContextINS2_11ProxyServerINS2_4test8messages12FooInterfaceEEEN5capnp11CallContextINSC_17CallFnAsyncParamsENSC_18CallFnAsyncResultsEEEEENS2_10ServerCallEJNS2_8TypeListIJEEEEEENSt9enable_ifIXsr3std7is_sameIDTclsrT_3getcldtdtfL1p1_12call_context9getParamsEEENS2_7Context6ReaderEEE5valueENS_7PromiseINT0_11CallContextEEEE4typeENS2_8PriorityILi1EEESM_RST_RKT1_DpOT2_ENUlvE_clEvEUlvE0_E3runEv /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/common.h:2007 (mptest+0x1f915d)
    bitcoin-core#14 _ZN2kj1_8DeferredIZZN2mp9PassFieldINS2_8AccessorINS2_10foo_fields7ContextELi17EEENS2_19ServerInvokeContextINS2_11ProxyServerINS2_4test8messages12FooInterfaceEEEN5capnp11CallContextINSC_17CallFnAsyncParamsENSC_18CallFnAsyncResultsEEEEENS2_10ServerCallEJNS2_8TypeListIJEEEEEENSt9enable_ifIXsr3std7is_sameIDTclsrT_3getcldtdtfL1p1_12call_context9getParamsEEENS2_7Context6ReaderEEE5valueENS_7PromiseINT0_11CallContextEEEE4typeENS2_8PriorityILi1EEESM_RST_RKT1_DpOT2_ENUlvE_clEvEUlvE0_ED2Ev /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/common.h:1996 (mptest+0x1f915d)
    bitcoin-core#15 _ZZN2mp9PassFieldINS_8AccessorINS_10foo_fields7ContextELi17EEENS_19ServerInvokeContextINS_11ProxyServerINS_4test8messages12FooInterfaceEEEN5capnp11CallContextINS9_17CallFnAsyncParamsENS9_18CallFnAsyncResultsEEEEENS_10ServerCallEJNS_8TypeListIJEEEEEENSt9enable_ifIXsr3std7is_sameIDTclsrT_3getcldtdtfL0p1_12call_context9getParamsEEENS_7Context6ReaderEEE5valueEN2kj7PromiseINT0_11CallContextEEEE4typeENS_8PriorityILi1EEESJ_RSR_RKT1_DpOT2_ENUlvE_clEv include/mp/type-context.h:122 (mptest+0x1f915d)
    bitcoin-core#16 _ZN2kj8FunctionIFvvEE4ImplIZN2mp9PassFieldINS4_8AccessorINS4_10foo_fields7ContextELi17EEENS4_19ServerInvokeContextINS4_11ProxyServerINS4_4test8messages12FooInterfaceEEEN5capnp11CallContextINSE_17CallFnAsyncParamsENSE_18CallFnAsyncResultsEEEEENS4_10ServerCallEJNS4_8TypeListIJEEEEEENSt9enable_ifIXsr3std7is_sameIDTclsrT_3getcldtdtfL0p1_12call_context9getParamsEEENS4_7Context6ReaderEEE5valueENS_7PromiseINT0_11CallContextEEEE4typeENS4_8PriorityILi1EEESO_RSV_RKT1_DpOT2_EUlvE_EclEv /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/function.h:142 (mptest+0x1f8e89)
    bitcoin-core#17 kj::Function<void ()>::operator()() /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/function.h:119 (mptest+0x1547f6)
    bitcoin-core#18 void mp::Unlock<std::unique_lock<std::mutex>, kj::Function<void ()>&>(std::unique_lock<std::mutex>&, kj::Function<void ()>&) include/mp/util.h:198 (mptest+0x1547f6)
    bitcoin-core#19 std::thread::_State_impl<std::thread::_Invoker<std::tuple<mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0> > >::_M_run() include/mp/proxy-io.h:294 (mptest+0x2266ca)
    bitcoin-core#20 execute_native_thread_routine ??:? (libstdc++.so.6+0xed063)

  Location is heap block of size 1024 at 0x726000012c00 allocated by thread T10:
    #0 operator new(unsigned long) ??:? (mptest+0x127c3c)
    bitcoin-core#1 mp::test::TestSetup::TestSetup(bool)::{lambda()bitcoin-core#1}::operator()() const /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/unique_ptr.h:1077 (mptest+0x130879)
    bitcoin-core#2 void std::__invoke_impl<void, mp::test::TestSetup::TestSetup(bool)::{lambda()bitcoin-core#1}>(std::__invoke_other, mp::test::TestSetup::TestSetup(bool)::{lambda()bitcoin-core#1}&&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/invoke.h:61 (mptest+0x130789)
    bitcoin-core#3 std::__invoke_result<mp::test::TestSetup::TestSetup(bool)::{lambda()bitcoin-core#1}>::type std::__invoke<mp::test::TestSetup::TestSetup(bool)::{lambda()bitcoin-core#1}>(mp::test::TestSetup::TestSetup(bool)::{lambda()bitcoin-core#1}&&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/invoke.h:96 (mptest+0x130789)
    bitcoin-core#4 void std::thread::_Invoker<std::tuple<mp::test::TestSetup::TestSetup(bool)::{lambda()bitcoin-core#1}> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_thread.h:301 (mptest+0x130789)
    bitcoin-core#5 std::thread::_Invoker<std::tuple<mp::test::TestSetup::TestSetup(bool)::{lambda()bitcoin-core#1}> >::operator()() /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_thread.h:308 (mptest+0x130789)
    bitcoin-core#6 std::thread::_State_impl<std::thread::_Invoker<std::tuple<mp::test::TestSetup::TestSetup(bool)::{lambda()bitcoin-core#1}> > >::_M_run() /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_thread.h:253 (mptest+0x130789)
    bitcoin-core#7 execute_native_thread_routine ??:? (libstdc++.so.6+0xed063)

  Mutex M0 (0x721c00003790) created at:
    #0 pthread_mutex_lock ??:? (mptest+0x9aa5c)
    bitcoin-core#1 __gthread_mutex_lock(pthread_mutex_t*) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/x86_64-unknown-linux-gnu/bits/gthr-default.h:762 (mptest+0x2265e2)
    bitcoin-core#2 std::mutex::lock() /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_mutex.h:113 (mptest+0x2265e2)
    bitcoin-core#3 std::unique_lock<std::mutex>::lock() /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/unique_lock.h:147 (mptest+0x2265e2)
    bitcoin-core#4 std::unique_lock<std::mutex>::unique_lock(std::mutex&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/unique_lock.h:73 (mptest+0x2265e2)
    bitcoin-core#5 mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0::operator()() const src/mp/proxy.cpp:399 (mptest+0x2265e2)
    bitcoin-core#6 void std::__invoke_impl<void, mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0>(std::__invoke_other, mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0&&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/invoke.h:61 (mptest+0x2265e2)
    bitcoin-core#7 std::__invoke_result<mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0>::type std::__invoke<mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0>(mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0&&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/invoke.h:96 (mptest+0x2265e2)
    bitcoin-core#8 void std::thread::_Invoker<std::tuple<mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_thread.h:301 (mptest+0x2265e2)
    bitcoin-core#9 std::thread::_Invoker<std::tuple<mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0> >::operator()() /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_thread.h:308 (mptest+0x2265e2)
    bitcoin-core#10 std::thread::_State_impl<std::thread::_Invoker<std::tuple<mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0> > >::_M_run() /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_thread.h:253 (mptest+0x2265e2)
    bitcoin-core#11 execute_native_thread_routine ??:? (libstdc++.so.6+0xed063)

  Thread T10 (tid=2025656, running) created by main thread at:
    #0 pthread_create ??:? (mptest+0x9a2a5)
    bitcoin-core#1 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State> >, void (*)()) ??:? (libstdc++.so.6+0xed138)
    bitcoin-core#2 mp::test::TestCase251::run() test/mp/test/test.cpp:271 (mptest+0x12b5bd)
    bitcoin-core#3 kj::Maybe<kj::Exception> kj::runCatchingExceptions<kj::TestRunner::run()::{lambda()bitcoin-core#1}>(kj::TestRunner::run()::{lambda()bitcoin-core#1}&&) ??:? (libkj-test.so.1.1.0+0x7290) (BuildId: 50ddf81234cd06daf5f2d3f11713ed193ade4eb7)

  Thread T11 (tid=2025657, running) created by thread T10 at:
    #0 pthread_create ??:? (mptest+0x9a2a5)
    bitcoin-core#1 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State> >, void (*)()) ??:? (libstdc++.so.6+0xed138)
    bitcoin-core#2 mp::ThreadMap::Server::dispatchCall(unsigned long, unsigned short, capnp::CallContext<capnp::AnyPointer, capnp::AnyPointer>) build-tsan/include/mp/proxy.capnp.c++:602 (mptest+0x220bb2)
    bitcoin-core#3 virtual thunk to mp::ThreadMap::Server::dispatchCall(unsigned long, unsigned short, capnp::CallContext<capnp::AnyPointer, capnp::AnyPointer>) build-tsan/include/mp/proxy.capnp.c++:? (mptest+0x220bb2)
    bitcoin-core#4 capnp::LocalClient::callInternal(unsigned long, unsigned short, capnp::CallContextHook&) ??:? (libcapnp-rpc.so.1.1.0+0x7551d) (BuildId: ec28fb7ea510d2e28e99522abd0ce44adde7cf44)
    bitcoin-core#5 mp::EventLoop::loop() src/mp/proxy.cpp:229 (mptest+0x222f60)
    bitcoin-core#6 mp::test::TestSetup::TestSetup(bool)::{lambda()bitcoin-core#1}::operator()() const test/mp/test/test.cpp:92 (mptest+0x130d23)
    bitcoin-core#7 void std::__invoke_impl<void, mp::test::TestSetup::TestSetup(bool)::{lambda()bitcoin-core#1}>(std::__invoke_other, mp::test::TestSetup::TestSetup(bool)::{lambda()bitcoin-core#1}&&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/invoke.h:61 (mptest+0x130789)
    bitcoin-core#8 std::__invoke_result<mp::test::TestSetup::TestSetup(bool)::{lambda()bitcoin-core#1}>::type std::__invoke<mp::test::TestSetup::TestSetup(bool)::{lambda()bitcoin-core#1}>(mp::test::TestSetup::TestSetup(bool)::{lambda()bitcoin-core#1}&&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/invoke.h:96 (mptest+0x130789)
    bitcoin-core#9 void std::thread::_Invoker<std::tuple<mp::test::TestSetup::TestSetup(bool)::{lambda()bitcoin-core#1}> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_thread.h:301 (mptest+0x130789)
    bitcoin-core#10 std::thread::_Invoker<std::tuple<mp::test::TestSetup::TestSetup(bool)::{lambda()bitcoin-core#1}> >::operator()() /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_thread.h:308 (mptest+0x130789)
    bitcoin-core#11 std::thread::_State_impl<std::thread::_Invoker<std::tuple<mp::test::TestSetup::TestSetup(bool)::{lambda()bitcoin-core#1}> > >::_M_run() /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_thread.h:253 (mptest+0x130789)
    bitcoin-core#12 execute_native_thread_routine ??:? (libstdc++.so.6+0xed063)

SUMMARY: ThreadSanitizer: data race src/mp/proxy.cpp:61 in mp::EventLoopRef::reset(bool)
==================
[ PASS ] test.cpp:251: Calling IPC method, disconnecting and blocking during the call (175646 μs)
5 test(s) passed
ThreadSanitizer: reported 1 warnings
nt method call interrupted by disconnect.
LOG0: {mptest-2025645/mptest-2025654} IPC server destroy N2mp11ProxyServerINS_4test8messages12FooInterfaceEEE
LOG0: {mptest-2025645/mptest-2025654} EventLoop::loop done, cancelling event listeners.
LOG0: {mptest-2025645/mptest-2025654} EventLoop::loop bye.
LOG0: {mptest-2025645/mptest-2025645} IPC client send FooInterface.add$Params (a = 1, b = 2)
LOG0: {mptest-2025645/mptest-2025656} IPC server recv request  bitcoin-core#35 FooInterface.add$Params (a = 1, b = 2)
LOG0: {mptest-2025645/mptest-2025656} IPC server send response bitcoin-core#35 FooInterface.add$Results (result = 3)
LOG0: {mptest-2025645/mptest-2025645} IPC client recv FooInterface.add$Results (result = 3)
LOG0: {mptest-2025645/mptest-2025645} IPC client send FooInterface.initThreadMap$Params (threadMap = <external capability>)
LOG0: {mptest-2025645/mptest-2025656} IPC server recv request  bitcoin-core#36 FooInterface.initThreadMap$Params (threadMap = <external capability>)
LOG0: {mptest-2025645/mptest-2025656} IPC server send response bitcoin-core#36 FooInterface.initThreadMap$Results (threadMap = <external capability>)
LOG0: {mptest-2025645/mptest-2025645} IPC client recv FooInterface.initThreadMap$Results (threadMap = <external capability>)
LOG0: {mptest-2025645/mptest-2025645} IPC client send FooInterface.callFnAsync$Params (context = (thread = <external capability>, callbackThread = <external capability>))
LOG0: {mptest-2025645/mptest-2025656} IPC server recv request  bitcoin-core#37 FooInterface.callFnAsync$Params (context = (thread = <external capability>, callbackThread = <external capability>))
LOG0: {mptest-2025645/mptest-2025656} IPC server post request  bitcoin-core#37 {mptest-2025645/mptest-2025657 (from mptest-2025645/mptest-2025645)}
LOG1: IPC client method call interrupted by disconnect.
LOG0: {mptest-2025645/mptest-2025656} IPC server send response bitcoin-core#37 FooInterface.callFnAsync$Results ()
LOG0: {mptest-2025645/mptest-2025656} IPC server destroy N2mp11ProxyServerINS_4test8messages12FooInterfaceEEE
LOG0: {mptest-2025645/mptest-2025656} EventLoop::loop done, cancelling event listeners.
LOG0: {mptest-2025645/mptest-2025656} EventLoop::loop bye.

0% tests passed, 1 tests failed out of 1

Total Test time (real) =   0.22 sec

The following tests FAILED:
	  1 - mptest (Failed)
Fix 3 TSAN data races exposed by previous commit. (Context is described in the
previous commit message.)

The first race condition happens setting `m_context.connection = nullptr` in
ProxyClientBase disconnect_cb callback without a lock, after a prior
`m_context.connection` pointer dereference in the
ProxyClient<Thread>::~ProxyClient() destructor in another thread. This is fixed
by adding using the `m_context.loop->m_mutex` mutex to guard acceses to the
`m_connection` variable.

The second race condition happens in the ~Connection destructor accessing the
`m_sync_cleanup_fns` without synchronization after a prior call to
removeSyncCleanup from ProxyClient<Thread>::~ProxyClient() in the other thread.
This is fixed by using a lock to guard access to `m_sync_cleanup_fns` here.
(Was already covered by this lock in the other functions).

The third race condition happens in the Connection destructor setting its
m_loop.m_loop pointer to null without synchronization after the
ProxyClient<Thread>::~ProxyClient() destructor dereferenced it earlier inside a
connection->removeSyncCleanup() call. The destructor will never actually make
that call if the Connection is destroyed because it checks to see if
m_disconnect_cb is null beforehand. However m_disconnect_cb is sometimes
accesed without synchronization so TSAN correctly reports a data race. This
problem is fixed by consistently guarding m_disconnect_cb with Waiter::m_mutex
locks. This fix also depends on the second fix above to make the ~Connection
destructor actually wait for the removeSyncCleanup() call to finish.

>>> Data race #1 accessing ProxyClientBase::m_context.connection <<<

[ TEST ] test.cpp:251: Calling IPC method, disconnecting and blocking during the call
LOG0: {mptest-2029510/mptest-2029510} IPC client send FooInterface.add$Params (a = 1, b = 2)
LOG0: {mptest-2029510/mptest-2029521} IPC server recv request  #35 FooInterface.add$Params (a = 1, b = 2)
LOG0: {mptest-2029510/mptest-2029521} IPC server send response #35 FooInterface.add$Results (result = 3)
LOG0: {mptest-2029510/mptest-2029510} IPC client recv FooInterface.add$Results (result = 3)
LOG0: {mptest-2029510/mptest-2029510} IPC client send FooInterface.initThreadMap$Params (threadMap = <external capability>)
LOG0: {mptest-2029510/mptest-2029521} IPC server recv request  #36 FooInterface.initThreadMap$Params (threadMap = <external capability>)
LOG0: {mptest-2029510/mptest-2029521} IPC server send response #36 FooInterface.initThreadMap$Results (threadMap = <external capability>)
LOG0: {mptest-2029510/mptest-2029510} IPC client recv FooInterface.initThreadMap$Results (threadMap = <external capability>)
LOG0: {mptest-2029510/mptest-2029510} IPC client send FooInterface.callFnAsync$Params (context = (thread = <external capability>, callbackThread = <external capability>))
LOG0: {mptest-2029510/mptest-2029521} IPC server recv request  #37 FooInterface.callFnAsync$Params (context = (thread = <external capability>, callbackThread = <external capability>))
LOG0: {mptest-2029510/mptest-2029521} IPC server post request  #37 {mptest-2029510/mptest-2029522 (from mptest-2029510/mptest-2029510)}
LOG1: IPC client method call interrupted by disconnect.
==================
WARNING: ThreadSanitizer: data race (pid=2029510)
  Write of size 8 at 0x722000004240 by thread T10:
    #0 mp::ProxyClientBase<mp::Thread, capnp::Void>::ProxyClientBase(mp::Thread::Client, mp::Connection*, bool)::{lambda()#1}::operator()() const include/mp/proxy-io.h:401 (mptest+0x2287c6)

   396      auto disconnect_cb = m_context.connection->addSyncCleanup([this]() {
   397          // Release client capability by move-assigning to temporary.
   398          {
   399              typename Interface::Client(std::move(m_client));
   400          }
   401          m_context.connection = nullptr;
   402      });

    #1 void std::__invoke_impl<void, mp::ProxyClientBase<mp::Thread, capnp::Void>::ProxyClientBase(mp::Thread::Client, mp::Connection*, bool)::{lambda()#1}&>(std::__invoke_other, mp::ProxyClientBase<mp::Thread, capnp::Void>::ProxyClientBase(mp::Thread::Client, mp::Connection*, bool)::{lambda()#1}&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/invoke.h:61 (mptest+0x2287c6)
    #2 std::enable_if<is_invocable_r_v<void, mp::ProxyClientBase<mp::Thread, capnp::Void>::ProxyClientBase(mp::Thread::Client, mp::Connection*, bool)::{lambda()#1}&>, void>::type std::__invoke_r<void, mp::ProxyClientBase<mp::Thread, capnp::Void>::ProxyClientBase(mp::Thread::Client, mp::Connection*, bool)::{lambda()#1}&>(mp::ProxyClientBase<mp::Thread, capnp::Void>::ProxyClientBase(mp::Thread::Client, mp::Connection*, bool)::{lambda()#1}&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/invoke.h:111 (mptest+0x2287c6)
    #3 std::_Function_handler<void (), mp::ProxyClientBase<mp::Thread, capnp::Void>::ProxyClientBase(mp::Thread::Client, mp::Connection*, bool)::{lambda()#1}>::_M_invoke(std::_Any_data const&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_function.h:290 (mptest+0x2287c6)
    #4 mp::Connection::~Connection() /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_function.h:591 (mptest+0x221a05)
    #5 kj::_::TransformPromiseNode<kj::_::Void, kj::_::Void, mp::test::TestSetup::TestSetup(bool)::{lambda()#1}::operator()() const::{lambda()#2}, kj::_::PropagateException>::getImpl(kj::_::ExceptionOrValue&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/unique_ptr.h:93 (mptest+0x133817)
    #6 kj::_::TransformPromiseNodeBase::get(kj::_::ExceptionOrValue&) ??:? (libkj-async.so.1.1.0+0x3da3d) (BuildId: 21ff3d5ab929b5e72be4dbbab1a9223f705af6e4)
    #7 mp::EventLoop::loop() src/mp/proxy.cpp:229 (mptest+0x223010)
    #8 mp::test::TestSetup::TestSetup(bool)::{lambda()#1}::operator()() const test/mp/test/test.cpp:92 (mptest+0x130d23)
    #9 void std::__invoke_impl<void, mp::test::TestSetup::TestSetup(bool)::{lambda()#1}>(std::__invoke_other, mp::test::TestSetup::TestSetup(bool)::{lambda()#1}&&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/invoke.h:61 (mptest+0x130789)
    #10 std::__invoke_result<mp::test::TestSetup::TestSetup(bool)::{lambda()#1}>::type std::__invoke<mp::test::TestSetup::TestSetup(bool)::{lambda()#1}>(mp::test::TestSetup::TestSetup(bool)::{lambda()#1}&&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/invoke.h:96 (mptest+0x130789)
    #11 void std::thread::_Invoker<std::tuple<mp::test::TestSetup::TestSetup(bool)::{lambda()#1}> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_thread.h:301 (mptest+0x130789)
    #12 std::thread::_Invoker<std::tuple<mp::test::TestSetup::TestSetup(bool)::{lambda()#1}> >::operator()() /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_thread.h:308 (mptest+0x130789)
    #13 std::thread::_State_impl<std::thread::_Invoker<std::tuple<mp::test::TestSetup::TestSetup(bool)::{lambda()#1}> > >::_M_run() /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_thread.h:253 (mptest+0x130789)
    #14 execute_native_thread_routine ??:? (libstdc++.so.6+0xed063)

  Previous read of size 8 at 0x722000004240 by thread T11 (mutexes: write M0):
    #0 mp::ProxyClient<mp::Thread>::~ProxyClient() src/mp/proxy.cpp:337 (mptest+0x22404d)

   336      if (m_disconnect_cb) {
   337          m_context.connection->removeSyncCleanup(*m_disconnect_cb);
   338      }

    #1 std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> >::~pair() /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/stl_iterator.h:3013 (mptest+0x1cadda)
    #2 void std::destroy_at<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >(std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> >*) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/stl_construct.h:88 (mptest+0x1cadda)
    #3 void std::allocator_traits<std::allocator<std::_Rb_tree_node<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > > > >::destroy<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >(std::allocator<std::_Rb_tree_node<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > > >&, std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> >*) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/alloc_traits.h:599 (mptest+0x1cadda)
    #4 std::_Rb_tree<mp::Connection*, std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> >, std::_Select1st<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >, std::less<mp::Connection*>, std::allocator<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > > >::_M_destroy_node(std::_Rb_tree_node<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >*) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/stl_tree.h:621 (mptest+0x1cadda)
    #5 std::_Rb_tree<mp::Connection*, std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> >, std::_Select1st<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >, std::less<mp::Connection*>, std::allocator<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > > >::_M_drop_node(std::_Rb_tree_node<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >*) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/stl_tree.h:629 (mptest+0x1cadda)
    #6 std::_Rb_tree<mp::Connection*, std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> >, std::_Select1st<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >, std::less<mp::Connection*>, std::allocator<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > > >::_M_erase(std::_Rb_tree_node<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >*) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/stl_tree.h:1934 (mptest+0x1cadda)
    #7 std::_Rb_tree<mp::Connection*, std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> >, std::_Select1st<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >, std::less<mp::Connection*>, std::allocator<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > > >::_M_erase_aux(std::_Rb_tree_const_iterator<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >, std::_Rb_tree_const_iterator<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/stl_tree.h:1251 (mptest+0x1cad31)
    #8 _ZZZN2mp9PassFieldINS_8AccessorINS_10foo_fields7ContextELi17EEENS_19ServerInvokeContextINS_11ProxyServerINS_4test8messages12FooInterfaceEEEN5capnp11CallContextINS9_17CallFnAsyncParamsENS9_18CallFnAsyncResultsEEEEENS_10ServerCallEJNS_8TypeListIJEEEEEENSt9enable_ifIXsr3std7is_sameIDTclsrT_3getcldtdtfL1p1_12call_context9getParamsEEENS_7Context6ReaderEEE5valueEN2kj7PromiseINT0_11CallContextEEEE4typeENS_8PriorityILi1EEESJ_RSR_RKT1_DpOT2_ENUlvE_clEvENKUlvE0_clEv /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/stl_tree.h:2519 (mptest+0x1f9c2e)
    #9 _ZN2kj1_8DeferredIZZN2mp9PassFieldINS2_8AccessorINS2_10foo_fields7ContextELi17EEENS2_19ServerInvokeContextINS2_11ProxyServerINS2_4test8messages12FooInterfaceEEEN5capnp11CallContextINSC_17CallFnAsyncParamsENSC_18CallFnAsyncResultsEEEEENS2_10ServerCallEJNS2_8TypeListIJEEEEEENSt9enable_ifIXsr3std7is_sameIDTclsrT_3getcldtdtfL1p1_12call_context9getParamsEEENS2_7Context6ReaderEEE5valueENS_7PromiseINT0_11CallContextEEEE4typeENS2_8PriorityILi1EEESM_RST_RKT1_DpOT2_ENUlvE_clEvEUlvE0_E3runEv /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/common.h:2007 (mptest+0x1f920d)
    #10 _ZN2kj1_8DeferredIZZN2mp9PassFieldINS2_8AccessorINS2_10foo_fields7ContextELi17EEENS2_19ServerInvokeContextINS2_11ProxyServerINS2_4test8messages12FooInterfaceEEEN5capnp11CallContextINSC_17CallFnAsyncParamsENSC_18CallFnAsyncResultsEEEEENS2_10ServerCallEJNS2_8TypeListIJEEEEEENSt9enable_ifIXsr3std7is_sameIDTclsrT_3getcldtdtfL1p1_12call_context9getParamsEEENS2_7Context6ReaderEEE5valueENS_7PromiseINT0_11CallContextEEEE4typeENS2_8PriorityILi1EEESM_RST_RKT1_DpOT2_ENUlvE_clEvEUlvE0_ED2Ev /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/common.h:1996 (mptest+0x1f920d)
    #11 _ZZN2mp9PassFieldINS_8AccessorINS_10foo_fields7ContextELi17EEENS_19ServerInvokeContextINS_11ProxyServerINS_4test8messages12FooInterfaceEEEN5capnp11CallContextINS9_17CallFnAsyncParamsENS9_18CallFnAsyncResultsEEEEENS_10ServerCallEJNS_8TypeListIJEEEEEENSt9enable_ifIXsr3std7is_sameIDTclsrT_3getcldtdtfL0p1_12call_context9getParamsEEENS_7Context6ReaderEEE5valueEN2kj7PromiseINT0_11CallContextEEEE4typeENS_8PriorityILi1EEESJ_RSR_RKT1_DpOT2_ENUlvE_clEv include/mp/type-context.h:122 (mptest+0x1f920d)
    #12 _ZN2kj8FunctionIFvvEE4ImplIZN2mp9PassFieldINS4_8AccessorINS4_10foo_fields7ContextELi17EEENS4_19ServerInvokeContextINS4_11ProxyServerINS4_4test8messages12FooInterfaceEEEN5capnp11CallContextINSE_17CallFnAsyncParamsENSE_18CallFnAsyncResultsEEEEENS4_10ServerCallEJNS4_8TypeListIJEEEEEENSt9enable_ifIXsr3std7is_sameIDTclsrT_3getcldtdtfL0p1_12call_context9getParamsEEENS4_7Context6ReaderEEE5valueENS_7PromiseINT0_11CallContextEEEE4typeENS4_8PriorityILi1EEESO_RSV_RKT1_DpOT2_EUlvE_EclEv /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/function.h:142 (mptest+0x1f8f39)
    #13 kj::Function<void ()>::operator()() /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/function.h:119 (mptest+0x154816)
    #14 void mp::Unlock<std::unique_lock<std::mutex>, kj::Function<void ()>&>(std::unique_lock<std::mutex>&, kj::Function<void ()>&) include/mp/util.h:198 (mptest+0x154816)
    #15 std::thread::_State_impl<std::thread::_Invoker<std::tuple<mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0> > >::_M_run() include/mp/proxy-io.h:294 (mptest+0x22677a)
    #16 execute_native_thread_routine ??:? (libstdc++.so.6+0xed063)

  Location is heap block of size 128 at 0x722000004200 allocated by thread T11:
    #0 operator new(unsigned long) ??:? (mptest+0x127c3c)
    #1 std::__new_allocator<std::_Rb_tree_node<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > > >::allocate(unsigned long, void const*) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/new_allocator.h:151 (mptest+0x2281e4)
    #2 std::allocator<std::_Rb_tree_node<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > > >::allocate(unsigned long) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/allocator.h:196 (mptest+0x2281e4)
    #3 std::allocator_traits<std::allocator<std::_Rb_tree_node<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > > > >::allocate(std::allocator<std::_Rb_tree_node<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > > >&, unsigned long) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/alloc_traits.h:515 (mptest+0x2281e4)
    #4 std::_Rb_tree<mp::Connection*, std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> >, std::_Select1st<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >, std::less<mp::Connection*>, std::allocator<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > > >::_M_get_node() /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/stl_tree.h:559 (mptest+0x2281e4)
    #5 std::_Rb_tree_node<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >* std::_Rb_tree<mp::Connection*, std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> >, std::_Select1st<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >, std::less<mp::Connection*>, std::allocator<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > > >::_M_create_node<std::piecewise_construct_t const&, std::tuple<mp::Connection*&>, std::tuple<mp::Thread::Client&&, mp::Connection*&, bool&&> >(std::piecewise_construct_t const&, std::tuple<mp::Connection*&>&&, std::tuple<mp::Thread::Client&&, mp::Connection*&, bool&&>&&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/stl_tree.h:609 (mptest+0x2281e4)
    #6 std::_Rb_tree<mp::Connection*, std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> >, std::_Select1st<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >, std::less<mp::Connection*>, std::allocator<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > > >::_Auto_node::_Auto_node<std::piecewise_construct_t const&, std::tuple<mp::Connection*&>, std::tuple<mp::Thread::Client&&, mp::Connection*&, bool&&> >(std::_Rb_tree<mp::Connection*, std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> >, std::_Select1st<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >, std::less<mp::Connection*>, std::allocator<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > > >&, std::piecewise_construct_t const&, std::tuple<mp::Connection*&>&&, std::tuple<mp::Thread::Client&&, mp::Connection*&, bool&&>&&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/stl_tree.h:1633 (mptest+0x228064)
    #7 std::pair<std::_Rb_tree_iterator<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >, bool> std::_Rb_tree<mp::Connection*, std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> >, std::_Select1st<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >, std::less<mp::Connection*>, std::allocator<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > > >::_M_emplace_unique<std::piecewise_construct_t const&, std::tuple<mp::Connection*&>, std::tuple<mp::Thread::Client&&, mp::Connection*&, bool&&> >(std::piecewise_construct_t const&, std::tuple<mp::Connection*&>&&, std::tuple<mp::Thread::Client&&, mp::Connection*&, bool&&>&&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/stl_tree.h:2432 (mptest+0x228064)
    #8 std::pair<std::_Rb_tree_iterator<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >, bool> std::map<mp::Connection*, mp::ProxyClient<mp::Thread>, std::less<mp::Connection*>, std::allocator<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > > >::emplace<std::piecewise_construct_t const&, std::tuple<mp::Connection*&>, std::tuple<mp::Thread::Client&&, mp::Connection*&, bool&&> >(std::piecewise_construct_t const&, std::tuple<mp::Connection*&>&&, std::tuple<mp::Thread::Client&&, mp::Connection*&, bool&&>&&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/stl_map.h:608 (mptest+0x223c07)
    #9 mp::SetThread(std::map<mp::Connection*, mp::ProxyClient<mp::Thread>, std::less<mp::Connection*>, std::allocator<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > > >&, std::mutex&, mp::Connection*, std::function<mp::Thread::Client ()> const&) src/mp/proxy.cpp:311 (mptest+0x223c07)
    #10 _ZZN2mp9PassFieldINS_8AccessorINS_10foo_fields7ContextELi17EEENS_19ServerInvokeContextINS_11ProxyServerINS_4test8messages12FooInterfaceEEEN5capnp11CallContextINS9_17CallFnAsyncParamsENS9_18CallFnAsyncResultsEEEEENS_10ServerCallEJNS_8TypeListIJEEEEEENSt9enable_ifIXsr3std7is_sameIDTclsrT_3getcldtdtfL0p1_12call_context9getParamsEEENS_7Context6ReaderEEE5valueEN2kj7PromiseINT0_11CallContextEEEE4typeENS_8PriorityILi1EEESJ_RSR_RKT1_DpOT2_ENUlvE_clEv include/mp/type-context.h:93 (mptest+0x1f912b)
    #11 _ZN2kj8FunctionIFvvEE4ImplIZN2mp9PassFieldINS4_8AccessorINS4_10foo_fields7ContextELi17EEENS4_19ServerInvokeContextINS4_11ProxyServerINS4_4test8messages12FooInterfaceEEEN5capnp11CallContextINSE_17CallFnAsyncParamsENSE_18CallFnAsyncResultsEEEEENS4_10ServerCallEJNS4_8TypeListIJEEEEEENSt9enable_ifIXsr3std7is_sameIDTclsrT_3getcldtdtfL0p1_12call_context9getParamsEEENS4_7Context6ReaderEEE5valueENS_7PromiseINT0_11CallContextEEEE4typeENS4_8PriorityILi1EEESO_RSV_RKT1_DpOT2_EUlvE_EclEv /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/function.h:142 (mptest+0x1f8f39)
    #12 kj::Function<void ()>::operator()() /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/function.h:119 (mptest+0x154816)
    #13 void mp::Unlock<std::unique_lock<std::mutex>, kj::Function<void ()>&>(std::unique_lock<std::mutex>&, kj::Function<void ()>&) include/mp/util.h:198 (mptest+0x154816)
    #14 std::thread::_State_impl<std::thread::_Invoker<std::tuple<mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0> > >::_M_run() include/mp/proxy-io.h:294 (mptest+0x22677a)
    #15 execute_native_thread_routine ??:? (libstdc++.so.6+0xed063)

  Mutex M0 (0x721c00003790) created at:
    #0 pthread_mutex_lock ??:? (mptest+0x9aa5c)
    #1 __gthread_mutex_lock(pthread_mutex_t*) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/x86_64-unknown-linux-gnu/bits/gthr-default.h:762 (mptest+0x226692)
    #2 std::mutex::lock() /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_mutex.h:113 (mptest+0x226692)
    #3 std::unique_lock<std::mutex>::lock() /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/unique_lock.h:147 (mptest+0x226692)
    #4 std::unique_lock<std::mutex>::unique_lock(std::mutex&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/unique_lock.h:73 (mptest+0x226692)
    #5 mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0::operator()() const src/mp/proxy.cpp:399 (mptest+0x226692)
    #6 void std::__invoke_impl<void, mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0>(std::__invoke_other, mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0&&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/invoke.h:61 (mptest+0x226692)
    #7 std::__invoke_result<mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0>::type std::__invoke<mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0>(mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0&&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/invoke.h:96 (mptest+0x226692)
    #8 void std::thread::_Invoker<std::tuple<mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_thread.h:301 (mptest+0x226692)
    #9 std::thread::_Invoker<std::tuple<mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0> >::operator()() /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_thread.h:308 (mptest+0x226692)
    #10 std::thread::_State_impl<std::thread::_Invoker<std::tuple<mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0> > >::_M_run() /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_thread.h:253 (mptest+0x226692)
    #11 execute_native_thread_routine ??:? (libstdc++.so.6+0xed063)

  Thread T10 (tid=2029521, running) created by main thread at:
    #0 pthread_create ??:? (mptest+0x9a2a5)
    #1 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State> >, void (*)()) ??:? (libstdc++.so.6+0xed138)
    #2 mp::test::TestCase251::run() test/mp/test/test.cpp:271 (mptest+0x12b5bd)
    #3 kj::Maybe<kj::Exception> kj::runCatchingExceptions<kj::TestRunner::run()::{lambda()#1}>(kj::TestRunner::run()::{lambda()#1}&&) ??:? (libkj-test.so.1.1.0+0x7290) (BuildId: 50ddf81234cd06daf5f2d3f11713ed193ade4eb7)

  Thread T11 (tid=2029522, running) created by thread T10 at:
    #0 pthread_create ??:? (mptest+0x9a2a5)
    #1 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State> >, void (*)()) ??:? (libstdc++.so.6+0xed138)
    #2 mp::ThreadMap::Server::dispatchCall(unsigned long, unsigned short, capnp::CallContext<capnp::AnyPointer, capnp::AnyPointer>) build-tsan/include/mp/proxy.capnp.c++:602 (mptest+0x220c62)
    #3 virtual thunk to mp::ThreadMap::Server::dispatchCall(unsigned long, unsigned short, capnp::CallContext<capnp::AnyPointer, capnp::AnyPointer>) build-tsan/include/mp/proxy.capnp.c++:? (mptest+0x220c62)
    #4 capnp::LocalClient::callInternal(unsigned long, unsigned short, capnp::CallContextHook&) ??:? (libcapnp-rpc.so.1.1.0+0x7551d) (BuildId: ec28fb7ea510d2e28e99522abd0ce44adde7cf44)
    #5 mp::EventLoop::loop() src/mp/proxy.cpp:229 (mptest+0x223010)
    #6 mp::test::TestSetup::TestSetup(bool)::{lambda()#1}::operator()() const test/mp/test/test.cpp:92 (mptest+0x130d23)
    #7 void std::__invoke_impl<void, mp::test::TestSetup::TestSetup(bool)::{lambda()#1}>(std::__invoke_other, mp::test::TestSetup::TestSetup(bool)::{lambda()#1}&&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/invoke.h:61 (mptest+0x130789)
    #8 std::__invoke_result<mp::test::TestSetup::TestSetup(bool)::{lambda()#1}>::type std::__invoke<mp::test::TestSetup::TestSetup(bool)::{lambda()#1}>(mp::test::TestSetup::TestSetup(bool)::{lambda()#1}&&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/invoke.h:96 (mptest+0x130789)
    #9 void std::thread::_Invoker<std::tuple<mp::test::TestSetup::TestSetup(bool)::{lambda()#1}> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_thread.h:301 (mptest+0x130789)
    #10 std::thread::_Invoker<std::tuple<mp::test::TestSetup::TestSetup(bool)::{lambda()#1}> >::operator()() /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_thread.h:308 (mptest+0x130789)
    #11 std::thread::_State_impl<std::thread::_Invoker<std::tuple<mp::test::TestSetup::TestSetup(bool)::{lambda()#1}> > >::_M_run() /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_thread.h:253 (mptest+0x130789)
    #12 execute_native_thread_routine ??:? (libstdc++.so.6+0xed063)

SUMMARY: ThreadSanitizer: data race include/mp/proxy-io.h:401 in mp::ProxyClientBase<mp::Thread, capnp::Void>::ProxyClientBase(mp::Thread::Client, mp::Connection*, bool)::{lambda()#1}::operator()() const
==================

>>> Data race #2 accessing Connection::m_sync_cleanup_fns <<<

==================
WARNING: ThreadSanitizer: data race (pid=2029510)
  Write of size 8 at 0x726000012ff8 by thread T10:
    #0 std::__cxx11::_List_base<std::function<void ()>, std::allocator<std::function<void ()> > >::_M_dec_size(unsigned long) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/stl_list.h:487 (mptest+0x221a20)
    #1 std::__cxx11::list<std::function<void ()>, std::allocator<std::function<void ()> > >::_M_erase(std::_List_iterator<std::function<void ()> >) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/stl_list.h:2023 (mptest+0x221a20)
    #2 std::__cxx11::list<std::function<void ()>, std::allocator<std::function<void ()> > >::pop_front() /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/stl_list.h:1301 (mptest+0x221a20)
    #3 mp::Connection::~Connection() src/mp/proxy.cpp:137 (mptest+0x221a20)

   135      while (!m_sync_cleanup_fns.empty()) {
   136          m_sync_cleanup_fns.front()();
   137          m_sync_cleanup_fns.pop_front();
   138      }

    #4 kj::_::TransformPromiseNode<kj::_::Void, kj::_::Void, mp::test::TestSetup::TestSetup(bool)::{lambda()#1}::operator()() const::{lambda()#2}, kj::_::PropagateException>::getImpl(kj::_::ExceptionOrValue&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/unique_ptr.h:93 (mptest+0x133817)
    #5 kj::_::TransformPromiseNodeBase::get(kj::_::ExceptionOrValue&) ??:? (libkj-async.so.1.1.0+0x3da3d) (BuildId: 21ff3d5ab929b5e72be4dbbab1a9223f705af6e4)
    #6 mp::EventLoop::loop() src/mp/proxy.cpp:229 (mptest+0x223010)
    #7 mp::test::TestSetup::TestSetup(bool)::{lambda()#1}::operator()() const test/mp/test/test.cpp:92 (mptest+0x130d23)
    #8 void std::__invoke_impl<void, mp::test::TestSetup::TestSetup(bool)::{lambda()#1}>(std::__invoke_other, mp::test::TestSetup::TestSetup(bool)::{lambda()#1}&&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/invoke.h:61 (mptest+0x130789)
    #9 std::__invoke_result<mp::test::TestSetup::TestSetup(bool)::{lambda()#1}>::type std::__invoke<mp::test::TestSetup::TestSetup(bool)::{lambda()#1}>(mp::test::TestSetup::TestSetup(bool)::{lambda()#1}&&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/invoke.h:96 (mptest+0x130789)
    #10 void std::thread::_Invoker<std::tuple<mp::test::TestSetup::TestSetup(bool)::{lambda()#1}> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_thread.h:301 (mptest+0x130789)
    #11 std::thread::_Invoker<std::tuple<mp::test::TestSetup::TestSetup(bool)::{lambda()#1}> >::operator()() /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_thread.h:308 (mptest+0x130789)
    #12 std::thread::_State_impl<std::thread::_Invoker<std::tuple<mp::test::TestSetup::TestSetup(bool)::{lambda()#1}> > >::_M_run() /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_thread.h:253 (mptest+0x130789)
    #13 execute_native_thread_routine ??:? (libstdc++.so.6+0xed063)

  Previous write of size 8 at 0x726000012ff8 by thread T11 (mutexes: write M0, write M1):
    #0 std::__cxx11::_List_base<std::function<void ()>, std::allocator<std::function<void ()> > >::_M_dec_size(unsigned long) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/stl_list.h:487 (mptest+0x221ea5)
    #1 std::__cxx11::list<std::function<void ()>, std::allocator<std::function<void ()> > >::_M_erase(std::_List_iterator<std::function<void ()> >) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/stl_list.h:2023 (mptest+0x221ea5)
    #2 std::__cxx11::list<std::function<void ()>, std::allocator<std::function<void ()> > >::erase(std::_List_const_iterator<std::function<void ()> >) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/list.tcc:158 (mptest+0x221ea5)
    #3 mp::Connection::removeSyncCleanup(std::_List_iterator<std::function<void ()> >) src/mp/proxy.cpp:158 (mptest+0x221ea5)

   155  void Connection::removeSyncCleanup(CleanupIt it)
   156  {
   157      const Lock lock(m_loop->m_mutex);
   158      m_sync_cleanup_fns.erase(it);
   159  }

    #4 mp::ProxyClient<mp::Thread>::~ProxyClient() src/mp/proxy.cpp:337 (mptest+0x224065)
    #5 std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> >::~pair() /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/stl_iterator.h:3013 (mptest+0x1cadda)
    #6 void std::destroy_at<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >(std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> >*) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/stl_construct.h:88 (mptest+0x1cadda)
    #7 void std::allocator_traits<std::allocator<std::_Rb_tree_node<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > > > >::destroy<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >(std::allocator<std::_Rb_tree_node<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > > >&, std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> >*) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/alloc_traits.h:599 (mptest+0x1cadda)
    #8 std::_Rb_tree<mp::Connection*, std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> >, std::_Select1st<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >, std::less<mp::Connection*>, std::allocator<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > > >::_M_destroy_node(std::_Rb_tree_node<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >*) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/stl_tree.h:621 (mptest+0x1cadda)
    #9 std::_Rb_tree<mp::Connection*, std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> >, std::_Select1st<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >, std::less<mp::Connection*>, std::allocator<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > > >::_M_drop_node(std::_Rb_tree_node<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >*) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/stl_tree.h:629 (mptest+0x1cadda)
    #10 std::_Rb_tree<mp::Connection*, std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> >, std::_Select1st<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >, std::less<mp::Connection*>, std::allocator<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > > >::_M_erase(std::_Rb_tree_node<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >*) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/stl_tree.h:1934 (mptest+0x1cadda)
    #11 std::_Rb_tree<mp::Connection*, std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> >, std::_Select1st<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >, std::less<mp::Connection*>, std::allocator<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > > >::_M_erase_aux(std::_Rb_tree_const_iterator<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >, std::_Rb_tree_const_iterator<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/stl_tree.h:1251 (mptest+0x1cad31)
    #12 _ZZZN2mp9PassFieldINS_8AccessorINS_10foo_fields7ContextELi17EEENS_19ServerInvokeContextINS_11ProxyServerINS_4test8messages12FooInterfaceEEEN5capnp11CallContextINS9_17CallFnAsyncParamsENS9_18CallFnAsyncResultsEEEEENS_10ServerCallEJNS_8TypeListIJEEEEEENSt9enable_ifIXsr3std7is_sameIDTclsrT_3getcldtdtfL1p1_12call_context9getParamsEEENS_7Context6ReaderEEE5valueEN2kj7PromiseINT0_11CallContextEEEE4typeENS_8PriorityILi1EEESJ_RSR_RKT1_DpOT2_ENUlvE_clEvENKUlvE0_clEv /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/stl_tree.h:2519 (mptest+0x1f9c2e)
    #13 _ZN2kj1_8DeferredIZZN2mp9PassFieldINS2_8AccessorINS2_10foo_fields7ContextELi17EEENS2_19ServerInvokeContextINS2_11ProxyServerINS2_4test8messages12FooInterfaceEEEN5capnp11CallContextINSC_17CallFnAsyncParamsENSC_18CallFnAsyncResultsEEEEENS2_10ServerCallEJNS2_8TypeListIJEEEEEENSt9enable_ifIXsr3std7is_sameIDTclsrT_3getcldtdtfL1p1_12call_context9getParamsEEENS2_7Context6ReaderEEE5valueENS_7PromiseINT0_11CallContextEEEE4typeENS2_8PriorityILi1EEESM_RST_RKT1_DpOT2_ENUlvE_clEvEUlvE0_E3runEv /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/common.h:2007 (mptest+0x1f920d)
    #14 _ZN2kj1_8DeferredIZZN2mp9PassFieldINS2_8AccessorINS2_10foo_fields7ContextELi17EEENS2_19ServerInvokeContextINS2_11ProxyServerINS2_4test8messages12FooInterfaceEEEN5capnp11CallContextINSC_17CallFnAsyncParamsENSC_18CallFnAsyncResultsEEEEENS2_10ServerCallEJNS2_8TypeListIJEEEEEENSt9enable_ifIXsr3std7is_sameIDTclsrT_3getcldtdtfL1p1_12call_context9getParamsEEENS2_7Context6ReaderEEE5valueENS_7PromiseINT0_11CallContextEEEE4typeENS2_8PriorityILi1EEESM_RST_RKT1_DpOT2_ENUlvE_clEvEUlvE0_ED2Ev /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/common.h:1996 (mptest+0x1f920d)
    #15 _ZZN2mp9PassFieldINS_8AccessorINS_10foo_fields7ContextELi17EEENS_19ServerInvokeContextINS_11ProxyServerINS_4test8messages12FooInterfaceEEEN5capnp11CallContextINS9_17CallFnAsyncParamsENS9_18CallFnAsyncResultsEEEEENS_10ServerCallEJNS_8TypeListIJEEEEEENSt9enable_ifIXsr3std7is_sameIDTclsrT_3getcldtdtfL0p1_12call_context9getParamsEEENS_7Context6ReaderEEE5valueEN2kj7PromiseINT0_11CallContextEEEE4typeENS_8PriorityILi1EEESJ_RSR_RKT1_DpOT2_ENUlvE_clEv include/mp/type-context.h:122 (mptest+0x1f920d)
    #16 _ZN2kj8FunctionIFvvEE4ImplIZN2mp9PassFieldINS4_8AccessorINS4_10foo_fields7ContextELi17EEENS4_19ServerInvokeContextINS4_11ProxyServerINS4_4test8messages12FooInterfaceEEEN5capnp11CallContextINSE_17CallFnAsyncParamsENSE_18CallFnAsyncResultsEEEEENS4_10ServerCallEJNS4_8TypeListIJEEEEEENSt9enable_ifIXsr3std7is_sameIDTclsrT_3getcldtdtfL0p1_12call_context9getParamsEEENS4_7Context6ReaderEEE5valueENS_7PromiseINT0_11CallContextEEEE4typeENS4_8PriorityILi1EEESO_RSV_RKT1_DpOT2_EUlvE_EclEv /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/function.h:142 (mptest+0x1f8f39)
    #17 kj::Function<void ()>::operator()() /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/function.h:119 (mptest+0x154816)
    #18 void mp::Unlock<std::unique_lock<std::mutex>, kj::Function<void ()>&>(std::unique_lock<std::mutex>&, kj::Function<void ()>&) include/mp/util.h:198 (mptest+0x154816)
    #19 std::thread::_State_impl<std::thread::_Invoker<std::tuple<mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0> > >::_M_run() include/mp/proxy-io.h:294 (mptest+0x22677a)
    #20 execute_native_thread_routine ??:? (libstdc++.so.6+0xed063)

  Location is heap block of size 1024 at 0x726000012c00 allocated by thread T10:
    #0 operator new(unsigned long) ??:? (mptest+0x127c3c)
    #1 mp::test::TestSetup::TestSetup(bool)::{lambda()#1}::operator()() const /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/unique_ptr.h:1077 (mptest+0x130879)
    #2 void std::__invoke_impl<void, mp::test::TestSetup::TestSetup(bool)::{lambda()#1}>(std::__invoke_other, mp::test::TestSetup::TestSetup(bool)::{lambda()#1}&&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/invoke.h:61 (mptest+0x130789)
    #3 std::__invoke_result<mp::test::TestSetup::TestSetup(bool)::{lambda()#1}>::type std::__invoke<mp::test::TestSetup::TestSetup(bool)::{lambda()#1}>(mp::test::TestSetup::TestSetup(bool)::{lambda()#1}&&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/invoke.h:96 (mptest+0x130789)
    #4 void std::thread::_Invoker<std::tuple<mp::test::TestSetup::TestSetup(bool)::{lambda()#1}> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_thread.h:301 (mptest+0x130789)
    #5 std::thread::_Invoker<std::tuple<mp::test::TestSetup::TestSetup(bool)::{lambda()#1}> >::operator()() /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_thread.h:308 (mptest+0x130789)
    #6 std::thread::_State_impl<std::thread::_Invoker<std::tuple<mp::test::TestSetup::TestSetup(bool)::{lambda()#1}> > >::_M_run() /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_thread.h:253 (mptest+0x130789)
    #7 execute_native_thread_routine ??:? (libstdc++.so.6+0xed063)

  Mutex M0 (0x721c00003790) created at:
    #0 pthread_mutex_lock ??:? (mptest+0x9aa5c)
    #1 __gthread_mutex_lock(pthread_mutex_t*) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/x86_64-unknown-linux-gnu/bits/gthr-default.h:762 (mptest+0x226692)
    #2 std::mutex::lock() /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_mutex.h:113 (mptest+0x226692)
    #3 std::unique_lock<std::mutex>::lock() /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/unique_lock.h:147 (mptest+0x226692)
    #4 std::unique_lock<std::mutex>::unique_lock(std::mutex&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/unique_lock.h:73 (mptest+0x226692)
    #5 mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0::operator()() const src/mp/proxy.cpp:399 (mptest+0x226692)
    #6 void std::__invoke_impl<void, mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0>(std::__invoke_other, mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0&&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/invoke.h:61 (mptest+0x226692)
    #7 std::__invoke_result<mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0>::type std::__invoke<mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0>(mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0&&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/invoke.h:96 (mptest+0x226692)
    #8 void std::thread::_Invoker<std::tuple<mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_thread.h:301 (mptest+0x226692)
    #9 std::thread::_Invoker<std::tuple<mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0> >::operator()() /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_thread.h:308 (mptest+0x226692)
    #10 std::thread::_State_impl<std::thread::_Invoker<std::tuple<mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0> > >::_M_run() /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_thread.h:253 (mptest+0x226692)
    #11 execute_native_thread_routine ??:? (libstdc++.so.6+0xed063)

  Mutex M1 (0x7f5d5d7fd510) created at:
    #0 pthread_mutex_lock ??:? (mptest+0x9aa5c)
    #1 __gthread_mutex_lock(pthread_mutex_t*) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/x86_64-unknown-linux-gnu/bits/gthr-default.h:762 (mptest+0x22131c)
    #2 std::mutex::lock() /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_mutex.h:113 (mptest+0x22131c)
    #3 std::unique_lock<std::mutex>::lock() /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/unique_lock.h:147 (mptest+0x22131c)
    #4 std::unique_lock<std::mutex>::unique_lock(std::mutex&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/unique_lock.h:73 (mptest+0x22131c)
    #5 mp::Lock::Lock(mp::Mutex&) include/mp/util.h:172 (mptest+0x22131c)
    #6 std::__detail::__variant::_Uninitialized<mp::Lock, false>::_Uninitialized<mp::Mutex&>(std::in_place_index_t<0ul>, mp::Mutex&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/variant:250 (mptest+0x22131c)
    #7 std::__detail::__variant::_Variadic_union<false, mp::Lock>::_Variadic_union<mp::Mutex&>(std::in_place_index_t<0ul>, mp::Mutex&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/variant:412 (mptest+0x22131c)
    #8 std::__detail::__variant::_Variadic_union<false, mp::Lock*, mp::Lock>::_Variadic_union<1ul, mp::Mutex&>(std::in_place_index_t<1ul>, mp::Mutex&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/variant:418 (mptest+0x22131c)
    #9 std::__detail::__variant::_Variant_storage<false, mp::Lock*, mp::Lock>::_Variant_storage<1ul, mp::Mutex&>(std::in_place_index_t<1ul>, mp::Mutex&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/variant:486 (mptest+0x22131c)
    #10 std::__detail::__variant::_Copy_ctor_base<false, mp::Lock*, mp::Lock>::_Variant_storage<1ul, mp::Mutex&>(std::in_place_index_t<1ul>, mp::Mutex&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/variant:584 (mptest+0x22131c)
    #11 std::__detail::__variant::_Move_ctor_base<false, mp::Lock*, mp::Lock>::_Variant_storage<1ul, mp::Mutex&>(std::in_place_index_t<1ul>, mp::Mutex&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/variant:621 (mptest+0x22131c)
    #12 std::__detail::__variant::_Copy_assign_base<false, mp::Lock*, mp::Lock>::_Variant_storage<1ul, mp::Mutex&>(std::in_place_index_t<1ul>, mp::Mutex&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/variant:659 (mptest+0x22131c)
    #13 std::__detail::__variant::_Move_assign_base<false, mp::Lock*, mp::Lock>::_Variant_storage<1ul, mp::Mutex&>(std::in_place_index_t<1ul>, mp::Mutex&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/variant:711 (mptest+0x22131c)
    #14 std::__detail::__variant::_Variant_base<mp::Lock*, mp::Lock>::_Variant_base<1ul, mp::Mutex&>(std::in_place_index_t<1ul>, mp::Mutex&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/variant:773 (mptest+0x22131c)
    #15 std::variant<mp::Lock*, mp::Lock>::variant<1ul, mp::Mutex&, mp::Lock, void>(std::in_place_index_t<1ul>, mp::Mutex&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/variant:1478 (mptest+0x22131c)
    #16 std::variant<mp::Lock*, mp::Lock>::variant<mp::Lock, mp::Mutex&, void>(std::in_place_type_t<mp::Lock>, mp::Mutex&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/variant:1458 (mptest+0x22131c)
    #17 mp::PtrOrValue<mp::Lock>::PtrOrValue<mp::Mutex&>(mp::Lock*, mp::Mutex&) include/mp/util.h:138 (mptest+0x22131c)
    #18 mp::EventLoopRef::EventLoopRef(mp::EventLoop&, mp::Lock*) src/mp/proxy.cpp:50 (mptest+0x22131c)
    #19 mp::Connection::Connection(mp::EventLoop&, kj::Own<kj::AsyncIoStream, decltype(nullptr)>&&, std::function<capnp::Capability::Client (mp::Connection&)> const&) include/mp/proxy-io.h:321 (mptest+0x1318e7)
    #20 mp::test::TestSetup::TestSetup(bool)::{lambda()#1}::operator()() const /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/unique_ptr.h:1077 (mptest+0x1308dc)
    #21 void std::__invoke_impl<void, mp::test::TestSetup::TestSetup(bool)::{lambda()#1}>(std::__invoke_other, mp::test::TestSetup::TestSetup(bool)::{lambda()#1}&&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/invoke.h:61 (mptest+0x130789)
    #22 std::__invoke_result<mp::test::TestSetup::TestSetup(bool)::{lambda()#1}>::type std::__invoke<mp::test::TestSetup::TestSetup(bool)::{lambda()#1}>(mp::test::TestSetup::TestSetup(bool)::{lambda()#1}&&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/invoke.h:96 (mptest+0x130789)
    #23 void std::thread::_Invoker<std::tuple<mp::test::TestSetup::TestSetup(bool)::{lambda()#1}> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_thread.h:301 (mptest+0x130789)
    #24 std::thread::_Invoker<std::tuple<mp::test::TestSetup::TestSetup(bool)::{lambda()#1}> >::operator()() /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_thread.h:308 (mptest+0x130789)
    #25 std::thread::_State_impl<std::thread::_Invoker<std::tuple<mp::test::TestSetup::TestSetup(bool)::{lambda()#1}> > >::_M_run() /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_thread.h:253 (mptest+0x130789)
    #26 execute_native_thread_routine ??:? (libstdc++.so.6+0xed063)

  Thread T10 (tid=2029521, running) created by main thread at:
    #0 pthread_create ??:? (mptest+0x9a2a5)
    #1 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State> >, void (*)()) ??:? (libstdc++.so.6+0xed138)
    #2 mp::test::TestCase251::run() test/mp/test/test.cpp:271 (mptest+0x12b5bd)
    #3 kj::Maybe<kj::Exception> kj::runCatchingExceptions<kj::TestRunner::run()::{lambda()#1}>(kj::TestRunner::run()::{lambda()#1}&&) ??:? (libkj-test.so.1.1.0+0x7290) (BuildId: 50ddf81234cd06daf5f2d3f11713ed193ade4eb7)

  Thread T11 (tid=2029522, running) created by thread T10 at:
    #0 pthread_create ??:? (mptest+0x9a2a5)
    #1 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State> >, void (*)()) ??:? (libstdc++.so.6+0xed138)
    #2 mp::ThreadMap::Server::dispatchCall(unsigned long, unsigned short, capnp::CallContext<capnp::AnyPointer, capnp::AnyPointer>) build-tsan/include/mp/proxy.capnp.c++:602 (mptest+0x220c62)
    #3 virtual thunk to mp::ThreadMap::Server::dispatchCall(unsigned long, unsigned short, capnp::CallContext<capnp::AnyPointer, capnp::AnyPointer>) build-tsan/include/mp/proxy.capnp.c++:? (mptest+0x220c62)
    #4 capnp::LocalClient::callInternal(unsigned long, unsigned short, capnp::CallContextHook&) ??:? (libcapnp-rpc.so.1.1.0+0x7551d) (BuildId: ec28fb7ea510d2e28e99522abd0ce44adde7cf44)
    #5 mp::EventLoop::loop() src/mp/proxy.cpp:229 (mptest+0x223010)
    #6 mp::test::TestSetup::TestSetup(bool)::{lambda()#1}::operator()() const test/mp/test/test.cpp:92 (mptest+0x130d23)
    #7 void std::__invoke_impl<void, mp::test::TestSetup::TestSetup(bool)::{lambda()#1}>(std::__invoke_other, mp::test::TestSetup::TestSetup(bool)::{lambda()#1}&&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/invoke.h:61 (mptest+0x130789)
    #8 std::__invoke_result<mp::test::TestSetup::TestSetup(bool)::{lambda()#1}>::type std::__invoke<mp::test::TestSetup::TestSetup(bool)::{lambda()#1}>(mp::test::TestSetup::TestSetup(bool)::{lambda()#1}&&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/invoke.h:96 (mptest+0x130789)
    #9 void std::thread::_Invoker<std::tuple<mp::test::TestSetup::TestSetup(bool)::{lambda()#1}> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_thread.h:301 (mptest+0x130789)
    #10 std::thread::_Invoker<std::tuple<mp::test::TestSetup::TestSetup(bool)::{lambda()#1}> >::operator()() /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_thread.h:308 (mptest+0x130789)
    #11 std::thread::_State_impl<std::thread::_Invoker<std::tuple<mp::test::TestSetup::TestSetup(bool)::{lambda()#1}> > >::_M_run() /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_thread.h:253 (mptest+0x130789)
    #12 execute_native_thread_routine ??:? (libstdc++.so.6+0xed063)

SUMMARY: ThreadSanitizer: data race src/mp/proxy.cpp:137 in mp::Connection::~Connection()
==================

>>> Data race #3 accesing Connection::m_loop::m_loop <<<

==================
WARNING: ThreadSanitizer: data race (pid=2029510)
  Write of size 8 at 0x726000012c00 by thread T10:
    #0 mp::EventLoopRef::reset(bool) src/mp/proxy.cpp:61 (mptest+0x22146f)

    58  void EventLoopRef::reset(bool relock) MP_NO_TSA
    59  {
    60      if (auto* loop{m_loop}) {
    61          m_loop = nullptr;
    62          auto loop_lock{PtrOrValue{m_lock, loop->m_mutex}};
    63          loop_lock->assert_locked(loop->m_mutex);
    64          assert(loop->m_num_clients > 0);
    65          loop->m_num_clients -= 1;

    #1 mp::Connection::~Connection() include/mp/proxy.h:58 (mptest+0x221b4b)

    58      ~EventLoopRef() { reset(); }

    #2 kj::_::TransformPromiseNode<kj::_::Void, kj::_::Void, mp::test::TestSetup::TestSetup(bool)::{lambda()#1}::operator()() const::{lambda()#2}, kj::_::PropagateException>::getImpl(kj::_::ExceptionOrValue&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/unique_ptr.h:93 (mptest+0x133817)
    #3 kj::_::TransformPromiseNodeBase::get(kj::_::ExceptionOrValue&) ??:? (libkj-async.so.1.1.0+0x3da3d) (BuildId: 21ff3d5ab929b5e72be4dbbab1a9223f705af6e4)
    #4 mp::EventLoop::loop() src/mp/proxy.cpp:229 (mptest+0x223010)

   228      for (;;) {
   229          const size_t read_bytes = wait_stream->read(&buffer, 0, 1).wait(m_io_context.waitScope);
   230          if (read_bytes != 1) throw std::logic_error("EventLoop wait_stream closed unexpectedly");

    #5 mp::test::TestSetup::TestSetup(bool)::{lambda()#1}::operator()() const test/mp/test/test.cpp:92 (mptest+0x130d23)
    #6 void std::__invoke_impl<void, mp::test::TestSetup::TestSetup(bool)::{lambda()#1}>(std::__invoke_other, mp::test::TestSetup::TestSetup(bool)::{lambda()#1}&&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/invoke.h:61 (mptest+0x130789)
    #7 std::__invoke_result<mp::test::TestSetup::TestSetup(bool)::{lambda()#1}>::type std::__invoke<mp::test::TestSetup::TestSetup(bool)::{lambda()#1}>(mp::test::TestSetup::TestSetup(bool)::{lambda()#1}&&) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/invoke.h:96 (mptest+0x130789)
    #8 void std::thread::_Invoker<std::tuple<mp::test::TestSetup::TestSetup(bool)::{lambda()#1}> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_thread.h:301 (mptest+0x130789)
    #9 std::thread::_Invoker<std::tuple<mp::test::TestSetup::TestSetup(bool)::{lambda()#1}> >::operator()() /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_thread.h:308 (mptest+0x130789)
    #10 std::thread::_State_impl<std::thread::_Invoker<std::tuple<mp::test::TestSetup::TestSetup(bool)::{lambda()#1}> > >::_M_run() /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/std_thread.h:253 (mptest+0x130789)
    #11 execute_native_thread_routine ??:? (libstdc++.so.6+0xed063)

  Previous read of size 8 at 0x726000012c00 by thread T11 (mutexes: write M0):
    #0 mp::Connection::removeSyncCleanup(std::_List_iterator<std::function<void ()> >) include/mp/proxy.h:60 (mptest+0x221e6f)

    60      EventLoop* operator->() const { assert(m_loop); return m_loop; }

    #1 mp::ProxyClient<mp::Thread>::~ProxyClient() src/mp/proxy.cpp:337 (mptest+0x224065)

   155  void Connection::removeSyncCleanup(CleanupIt it)
   156  {
   157      const Lock lock(m_loop->m_mutex);
   158      m_sync_cleanup_fns.erase(it);
   159  }
   ...
   336      if (m_disconnect_cb) {
   337          m_context.connection->removeSyncCleanup(*m_disconnect_cb);
   338      }

    #2 std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> >::~pair() /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/stl_iterator.h:3013 (mptest+0x1cadda)
    #3 void std::destroy_at<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >(std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> >*) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/stl_construct.h:88 (mptest+0x1cadda)
    #4 void std::allocator_traits<std::allocator<std::_Rb_tree_node<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > > > >::destroy<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >(std::allocator<std::_Rb_tree_node<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > > >&, std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> >*) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/alloc_traits.h:599 (mptest+0x1cadda)
    #5 std::_Rb_tree<mp::Connection*, std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> >, std::_Select1st<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >, std::less<mp::Connection*>, std::allocator<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > > >::_M_destroy_node(std::_Rb_tree_node<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >*) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/stl_tree.h:621 (mptest+0x1cadda)
    #6 std::_Rb_tree<mp::Connection*, std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> >, std::_Select1st<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >, std::less<mp::Connection*>, std::allocator<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > > >::_M_drop_node(std::_Rb_tree_node<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >*) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/stl_tree.h:629 (mptest+0x1cadda)
    #7 std::_Rb_tree<mp::Connection*, std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> >, std::_Select1st<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >, std::less<mp::Connection*>, std::allocator<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > > >::_M_erase(std::_Rb_tree_node<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >*) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/stl_tree.h:1934 (mptest+0x1cadda)
    #8 std::_Rb_tree<mp::Connection*, std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> >, std::_Select1st<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >, std::less<mp::Connection*>, std::allocator<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > > >::_M_erase_aux(std::_Rb_tree_const_iterator<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >, std::_Rb_tree_const_iterator<std::pair<mp::Connection* const, mp::ProxyClient<mp::Thread> > >) /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/stl_tree.h:1251 (mptest+0x1cad31)
    #9 _ZZZN2mp9PassFieldINS_8AccessorINS_10foo_fields7ContextELi17EEENS_19ServerInvokeContextINS_11ProxyServerINS_4test8messages12FooInterfaceEEEN5capnp11CallContextINS9_17CallFnAsyncParamsENS9_18CallFnAsyncResultsEEEEENS_10ServerCallEJNS_8TypeListIJEEEEEENSt9enable_ifIXsr3std7is_sameIDTclsrT_3getcldtdtfL1p1_12call_context9getParamsEEENS_7Context6ReaderEEE5valueEN2kj7PromiseINT0_11CallContextEEEE4typeENS_8PriorityILi1EEESJ_RSR_RKT1_DpOT2_ENUlvE_clEvENKUlvE0_clEv /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.20250322/include/c++/14.2.1.20250322/bits/stl_tree.h:2519 (mptest+0x1f9c2e)
    #10 _ZN2kj1_8DeferredIZZN2mp9PassFieldINS2_8AccessorINS2_10foo_fields7ContextELi17EEENS2_19ServerInvokeContextINS2_11ProxyServerINS2_4test8messages12FooInterfaceEEEN5capnp11CallContextINSC_17CallFnAsyncParamsENSC_18CallFnAsyncResultsEEEEENS2_10ServerCallEJNS2_8TypeListIJEEEEEENSt9enable_ifIXsr3std7is_sameIDTclsrT_3getcldtdtfL1p1_12call_context9getParamsEEENS2_7Context6ReaderEEE5valueENS_7PromiseINT0_11CallContextEEEE4typeENS2_8PriorityILi1EEESM_RST_RKT1_DpOT2_ENUlvE_clEvEUlvE0_E3runEv /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/common.h:2007 (mptest+0x1f920d)
    #11 _ZN2kj1_8DeferredIZZN2mp9PassFieldINS2_8AccessorINS2_10foo_fields7ContextELi17EEENS2_19ServerInvokeContextINS2_11ProxyServerINS2_4test8messages12FooInterfaceEEEN5capnp11CallContextINSC_17CallFnAsyncParamsENSC_18CallFnAsyncResultsEEEEENS2_10ServerCallEJNS2_8TypeListIJEEEEEENSt9enable_ifIXsr3std7is_sameIDTclsrT_3getcldtdtfL1p1_12call_context9getParamsEEENS2_7Context6ReaderEEE5valueENS_7PromiseINT0_11CallContextEEEE4typeENS2_8PriorityILi1EEESM_RST_RKT1_DpOT2_ENUlvE_clEvEUlvE0_ED2Ev /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/common.h:1996 (mptest+0x1f920d)
    #12 _ZZN2mp9PassFieldINS_8AccessorINS_10foo_fields7ContextELi17EEENS_19ServerInvokeContextINS_11ProxyServerINS_4test8messages12FooInterfaceEEEN5capnp11CallContextINS9_17CallFnAsyncParamsENS9_18CallFnAsyncResultsEEEEENS_10ServerCallEJNS_8TypeListIJEEEEEENSt9enable_ifIXsr3std7is_sameIDTclsrT_3getcldtdtfL0p1_12call_context9getParamsEEENS_7Context6ReaderEEE5valueEN2kj7PromiseINT0_11CallContextEEEE4typeENS_8PriorityILi1EEESJ_RSR_RKT1_DpOT2_ENUlvE_clEv include/mp/type-context.h:122 (mptest+0x1f920d)

   104                      KJ_DEFER({
   105                          std::unique_lock<std::mutex> lock(thread_context.waiter->m_mutex);
   106                          // Call erase here with a Connection* argument instead
   107                          // of an iterator argument, because the `request_thread`
   108                          // iterator may be invalid if the connection is closed
   109                          // during this function call. More specifically, the
   110                          // iterator may be invalid because SetThread adds a
   111                          // cleanup callback to the Connection destructor that
   112                          // erases the thread from the map, and also because the
   113                          // ProxyServer<Thread> destructor calls
   114                          // request_threads.clear().
   115                          if (erase_thread) {
   116                              disconnected = !request_threads.erase(server.m_context.connection);
   117                          } else {
   118                              disconnected = !request_threads.count(server.m_context.connection);
   119                          }
   120                      });
   121                      fn.invoke(server_context, args...);
   122                  }

    #13 _ZN2kj8FunctionIFvvEE4ImplIZN2mp9PassFieldINS4_8AccessorINS4_10foo_fields7ContextELi17EEENS4_19ServerInvokeContextINS4_11ProxyServerINS4_4test8messages12FooInterfaceEEEN5capnp11CallContextINSE_17CallFnAsyncParamsENSE_18CallFnAsyncResultsEEEEENS4_10ServerCallEJNS4_8TypeListIJEEEEEENSt9enable_ifIXsr3std7is_sameIDTclsrT_3getcldtdtfL0p1_12call_context9getParamsEEENS4_7Context6ReaderEEE5valueENS_7PromiseINT0_11CallContextEEEE4typeENS4_8PriorityILi1EEESO_RSV_RKT1_DpOT2_EUlvE_EclEv /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/function.h:142 (mptest+0x1f8f39)
    #14 kj::Function<void ()>::operator()() /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/function.h:119 (mptest+0x154816)
    #15 void mp::Unlock<std::unique_lock<std::mutex>, kj::Function<void ()>&>(std::unique_lock<std::mutex>&, kj::Function<void ()>&) include/mp/util.h:198 (mptest+0x154816)
    #16 std::thread::_State_impl<std::thread::_Invoker<std::tuple<mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0> > >::_M_run() include/mp/proxy-io.h:294 (mptest+0x22677a)
    #17 execute_native_thread_routine ??:? (libstdc++.so.6+0xed063)

  Location is heap block of size 1024 at 0x726000012c00 allocated by thread T10:
    #0 operator new(unsigned long) ??:? (mptest+0x127c3c)
    #1 mp::test::TestSetup::TestSetup(bool)::{lambda()#1}::operator()() const /nix/store/9ds850ifd4jwcccpp3v14818kk74ldf2-gcc-14.2.1.202503…
Fix MemorySanitizer: use-of-uninitialized-value error in "disconnecting and
blocking" promise::get_future call reported:

- Sjors/bitcoin#90 (comment)
- https://cirrus-ci.com/task/4658925234028544and

and fixed:

- Sjors/bitcoin#90 (comment)

An issue exists to add an MSAN CI job to catch errors like this more quickly in
the future bitcoin-core#188

Error looks like:

[ TEST ] test.cpp:251: Calling IPC method, disconnecting and blocking during the call
...
MemorySanitizer: use-of-uninitialized-value
    #0 0x7f83ecb19853 in std::__1::promise<void>::get_future() /msan/llvm-project/libcxx/src/future.cpp:154:16
    bitcoin-core#1 0x55bf563af03c in mp::test::TestCase251::run()::$_0::operator()() const /ci_container_base/ci/scratch/build-x86_64-pc-linux-gnu/src/ipc/libmultiprocess/test/./ipc/libmultiprocess/test/mp/test/test.cpp:280:16
    bitcoin-core#2 0x55bf563af03c in decltype(std::declval<mp::test::TestCase251::run()::$_0&>()()) std::__1::__invoke[abi:de200100]<mp::test::TestCase251::run()::$_0&>(mp::test::TestCase251::run()::$_0&) /msan/cxx_build/include/c++/v1/__type_traits/invoke.h:179:25
    bitcoin-core#3 0x55bf563af03c in void std::__1::__invoke_void_return_wrapper<void, true>::__call[abi:de200100]<mp::test::TestCase251::run()::$_0&>(mp::test::TestCase251::run()::$_0&) /msan/cxx_build/include/c++/v1/__type_traits/invoke.h:251:5
    bitcoin-core#4 0x55bf563af03c in void std::__1::__invoke_r[abi:de200100]<void, mp::test::TestCase251::run()::$_0&>(mp::test::TestCase251::run()::$_0&) /msan/cxx_build/include/c++/v1/__type_traits/invoke.h:273:10
    bitcoin-core#5 0x55bf563af03c in std::__1::__function::__alloc_func<mp::test::TestCase251::run()::$_0, std::__1::allocator<mp::test::TestCase251::run()::$_0>, void ()>::operator()[abi:de200100]() /msan/cxx_build/include/c++/v1/__functional/function.h:167:12
    bitcoin-core#6 0x55bf563af03c in std::__1::__function::__func<mp::test::TestCase251::run()::$_0, std::__1::allocator<mp::test::TestCase251::run()::$_0>, void ()>::operator()() /msan/cxx_build/include/c++/v1/__functional/function.h:319:10
    bitcoin-core#7 0x55bf565f1b25 in std::__1::__function::__value_func<void ()>::operator()[abi:de200100]() const /msan/cxx_build/include/c++/v1/__functional/function.h:436:12
    bitcoin-core#8 0x55bf565f1b25 in std::__1::function<void ()>::operator()() const /msan/cxx_build/include/c++/v1/__functional/function.h:995:10
    bitcoin-core#9 0x55bf565f1b25 in mp::test::FooImplementation::callFnAsync() /ci_container_base/ci/scratch/build-x86_64-pc-linux-gnu/src/ipc/libmultiprocess/test/./ipc/libmultiprocess/test/mp/test/foo.h:83:40
    bitcoin-core#10 0x55bf565f1b25 in decltype(auto) mp::ProxyMethodTraits<mp::test::messages::FooInterface::CallFnAsyncParams, void>::invoke<mp::ServerInvokeContext<mp::ProxyServer<mp::test::messages::FooInterface>, capnp::CallContext<mp::test::messages::FooInterface::CallFnAsyncParams, mp::test::messages::FooInterface::CallFnAsyncResults>>>(mp::ServerInvokeContext<mp::ProxyServer<mp::test::messages::FooInterface>, capnp::CallContext<mp::test::messages::FooInterface::CallFnAsyncParams, mp::test::messages::FooInterface::CallFnAsyncResults>>&) /ci_container_base/ci/scratch/build-x86_64-pc-linux-gnu/src/ipc/libmultiprocess/test/./ipc/libmultiprocess/include/mp/proxy.h:288:16
    bitcoin-core#11 0x55bf565f1b25 in decltype(auto) mp::ServerCall::invoke<mp::ServerInvokeContext<mp::ProxyServer<mp::test::messages::FooInterface>, capnp::CallContext<mp::test::messages::FooInterface::CallFnAsyncParams, mp::test::messages::FooInterface::CallFnAsyncResults>>>(mp::ServerInvokeContext<mp::ProxyServer<mp::test::messages::FooInterface>, capnp::CallContext<mp::test::messages::FooInterface::CallFnAsyncParams, mp::test::messages::FooInterface::CallFnAsyncResults>>&, mp::TypeList<>) const /ci_container_base/ci/scratch/build-x86_64-pc-linux-gnu/src/ipc/libmultiprocess/test/./ipc/libmultiprocess/include/mp/proxy-types.h:448:16
    bitcoin-core#12 0x55bf565f1b25 in std::__1::enable_if<std::is_same<decltype(mp::Accessor<mp::foo_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<mp::test::messages::FooInterface>, capnp::CallContext<mp::test::messages::FooInterface::CallFnAsyncParams, mp::test::messages::FooInterface::CallFnAsyncResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::foo_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<mp::test::messages::FooInterface>, capnp::CallContext<mp::test::messages::FooInterface::CallFnAsyncParams, mp::test::messages::FooInterface::CallFnAsyncResults>>, mp::ServerCall, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<mp::test::messages::FooInterface>, capnp::CallContext<mp::test::messages::FooInterface::CallFnAsyncParams, mp::test::messages::FooInterface::CallFnAsyncResults>>&, mp::ServerCall const&, mp::TypeList<>&&)::'lambda'()::operator()() /ci_container_base/ci/scratch/build-x86_64-pc-linux-gnu/src/ipc/libmultiprocess/test/./ipc/libmultiprocess/include/mp/type-context.h:121:24
    bitcoin-core#13 0x55bf565f12d1 in kj::Function<void ()>::Impl<std::__1::enable_if<std::is_same<decltype(mp::Accessor<mp::foo_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<mp::test::messages::FooInterface>, capnp::CallContext<mp::test::messages::FooInterface::CallFnAsyncParams, mp::test::messages::FooInterface::CallFnAsyncResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::foo_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<mp::test::messages::FooInterface>, capnp::CallContext<mp::test::messages::FooInterface::CallFnAsyncParams, mp::test::messages::FooInterface::CallFnAsyncResults>>, mp::ServerCall, mp::TypeList<>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<mp::test::messages::FooInterface>, capnp::CallContext<mp::test::messages::FooInterface::CallFnAsyncParams, mp::test::messages::FooInterface::CallFnAsyncResults>>&, mp::ServerCall const&, mp::TypeList<>&&)::'lambda'()>::operator()() /ci_container_base/depends/x86_64-pc-linux-gnu/include/kj/function.h:142:14
    bitcoin-core#14 0x55bf5641125e in kj::Function<void ()>::operator()() /ci_container_base/depends/x86_64-pc-linux-gnu/include/kj/function.h:119:12
    bitcoin-core#15 0x55bf5641125e in void mp::Unlock<std::__1::unique_lock<std::__1::mutex>, kj::Function<void ()>&>(std::__1::unique_lock<std::__1::mutex>&, kj::Function<void ()>&) /ci_container_base/ci/scratch/build-x86_64-pc-linux-gnu/src/ipc/libmultiprocess/test/./ipc/libmultiprocess/include/mp/util.h:198:5
    bitcoin-core#16 0x55bf5667e45b in void mp::Waiter::wait<mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0::operator()() const::'lambda'()>(std::__1::unique_lock<std::__1::mutex>&, mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0::operator()() const::'lambda'())::'lambda'()::operator()() const /ci_container_base/ci/scratch/build-x86_64-pc-linux-gnu/src/ipc/libmultiprocess/./ipc/libmultiprocess/include/mp/proxy-io.h:294:17
    bitcoin-core#17 0x55bf5667e45b in void std::__1::condition_variable::wait<void mp::Waiter::wait<mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0::operator()() const::'lambda'()>(std::__1::unique_lock<std::__1::mutex>&, mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0::operator()() const::'lambda'())::'lambda'()>(std::__1::unique_lock<std::__1::mutex>&, mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0::operator()() const::'lambda'()) /msan/cxx_build/include/c++/v1/__condition_variable/condition_variable.h:146:11
    bitcoin-core#18 0x55bf5667e45b in void mp::Waiter::wait<mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0::operator()() const::'lambda'()>(std::__1::unique_lock<std::__1::mutex>&, mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0::operator()() const::'lambda'()) /ci_container_base/ci/scratch/build-x86_64-pc-linux-gnu/src/ipc/libmultiprocess/./ipc/libmultiprocess/include/mp/proxy-io.h:285:14
    bitcoin-core#19 0x55bf5667e45b in mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0::operator()() const /ci_container_base/ci/scratch/build-x86_64-pc-linux-gnu/src/ipc/libmultiprocess/./ipc/libmultiprocess/src/mp/proxy.cpp:404:34
    bitcoin-core#20 0x55bf5667e45b in decltype(std::declval<mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0>()()) std::__1::__invoke[abi:de200100]<mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0>(mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0&&) /msan/cxx_build/include/c++/v1/__type_traits/invoke.h:179:25
    bitcoin-core#21 0x55bf5667e45b in void std::__1::__thread_execute[abi:de200100]<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct>>, mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0>(std::__1::tuple<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct>>, mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0>&, std::__1::__tuple_indices<...>) /msan/cxx_build/include/c++/v1/__thread/thread.h:199:3
    bitcoin-core#22 0x55bf5667e45b in void* std::__1::__thread_proxy[abi:de200100]<std::__1::tuple<std::__1::unique_ptr<std::__1::__thread_struct, std::__1::default_delete<std::__1::__thread_struct>>, mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0>>(void*) /msan/cxx_build/include/c++/v1/__thread/thread.h:208:3
    bitcoin-core#23 0x7f83ec69caa3  (/lib/x86_64-linux-gnu/libc.so.6+0x9caa3) (BuildId: 42c84c92e6f98126b3e2230ebfdead22c235b667)
    bitcoin-core#24 0x7f83ec729c3b  (/lib/x86_64-linux-gnu/libc.so.6+0x129c3b) (BuildId: 42c84c92e6f98126b3e2230ebfdead22c235b667)
[11:46:33.109]
  Member fields were destroyed
    #0 0x55bf56348abd in __sanitizer_dtor_callback_fields /msan/llvm-project/compiler-rt/lib/msan/msan_interceptors.cpp:1044:5
    bitcoin-core#1 0x7f83ecb196de in ~promise /msan/cxx_build/include/c++/v1/future:1341:22
    bitcoin-core#2 0x7f83ecb196de in std::__1::promise<void>::~promise() /msan/llvm-project/libcxx/src/future.cpp:151:1
    bitcoin-core#3 0x55bf563adb36 in mp::test::TestCase251::run() /ci_container_base/ci/scratch/build-x86_64-pc-linux-gnu/src/ipc/libmultiprocess/test/./ipc/libmultiprocess/test/mp/test/test.cpp:293:1
    bitcoin-core#4 0x55bf5669252e in kj::TestRunner::run()::'lambda'()::operator()() const /usr/src/kj/test.c++:318:11
    bitcoin-core#5 0x55bf5669252e in kj::Maybe<kj::Exception> kj::runCatchingExceptions<kj::TestRunner::run()::'lambda'()>(kj::TestRunner::run()::'lambda'()&&) /usr/src/kj/exception.h:371:5
    bitcoin-core#6 0x55bf5669071e in kj::TestRunner::run() /usr/src/kj/test.c++:318:11
    bitcoin-core#7 0x55bf5668f977 in auto kj::TestRunner::getMain()::'lambda5'(auto&, auto&&...)::operator()<kj::TestRunner>(auto&, auto&&...) /usr/src/kj/test.c++:217:27
    bitcoin-core#8 0x55bf5668f977 in auto kj::_::BoundMethod<kj::TestRunner&, kj::TestRunner::getMain()::'lambda5'(auto&, auto&&...), kj::TestRunner::getMain()::'lambda6'(auto&, auto&&...)>::operator()<>() /usr/src/kj/function.h:263:12
    bitcoin-core#9 0x55bf5668f977 in kj::Function<kj::MainBuilder::Validity ()>::Impl<kj::_::BoundMethod<kj::TestRunner&, kj::TestRunner::getMain()::'lambda5'(auto&, auto&&...), kj::TestRunner::getMain()::'lambda6'(auto&, auto&&...)>>::operator()() /usr/src/kj/function.h:142:14
    bitcoin-core#10 0x55bf56b7362c in kj::Function<kj::MainBuilder::Validity ()>::operator()() /usr/src/kj/function.h:119:12
    bitcoin-core#11 0x55bf56b7362c in kj::MainBuilder::MainImpl::operator()(kj::StringPtr, kj::ArrayPtr<kj::StringPtr const>) /usr/src/kj/main.c++:623:5
    bitcoin-core#12 0x55bf56b8865c in kj::Function<void (kj::StringPtr, kj::ArrayPtr<kj::StringPtr const>)>::Impl<kj::MainBuilder::MainImpl>::operator()(kj::StringPtr, kj::ArrayPtr<kj::StringPtr const>) /usr/src/kj/function.h:142:14
    bitcoin-core#13 0x55bf56b6a592 in kj::Function<void (kj::StringPtr, kj::ArrayPtr<kj::StringPtr const>)>::operator()(kj::StringPtr, kj::ArrayPtr<kj::StringPtr const>) /usr/src/kj/function.h:119:12
    bitcoin-core#14 0x55bf56b6a592 in kj::runMainAndExit(kj::ProcessContext&, kj::Function<void (kj::StringPtr, kj::ArrayPtr<kj::StringPtr const>)>&&, int, char**)::$_0::operator()() const /usr/src/kj/main.c++:228:5
    bitcoin-core#15 0x55bf56b6a592 in kj::Maybe<kj::Exception> kj::runCatchingExceptions<kj::runMainAndExit(kj::ProcessContext&, kj::Function<void (kj::StringPtr, kj::ArrayPtr<kj::StringPtr const>)>&&, int, char**)::$_0>(kj::runMainAndExit(kj::ProcessContext&, kj::Function<void (kj::StringPtr, kj::ArrayPtr<kj::StringPtr const>)>&&, int, char**)::$_0&&) /usr/src/kj/exception.h:371:5
    bitcoin-core#16 0x55bf56b69b5d in kj::runMainAndExit(kj::ProcessContext&, kj::Function<void (kj::StringPtr, kj::ArrayPtr<kj::StringPtr const>)>&&, int, char**) /usr/src/kj/main.c++:228:5
    bitcoin-core#17 0x55bf5668be8f in main /usr/src/kj/test.c++:381:1
    bitcoin-core#18 0x7f83ec62a1c9  (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 42c84c92e6f98126b3e2230ebfdead22c235b667)
    bitcoin-core#19 0x7f83ec62a28a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 42c84c92e6f98126b3e2230ebfdead22c235b667)
    bitcoin-core#20 0x55bf5630b374 in _start (/ci_container_base/ci/scratch/build-x86_64-pc-linux-gnu/src/ipc/libmultiprocess/test/mptest+0x77374)
[11:46:33.109]
SUMMARY: MemorySanitizer: use-of-uninitialized-value /msan/llvm-project/libcxx/src/future.cpp:154:16 in std::__1::promise<void>::get_future()
Exiting
@ryanofsky
Copy link
Collaborator Author

ryanofsky commented Jun 28, 2025

Updated 258b83c -> 71d3107 (pr/tsan.3 -> pr/tsan.4, compare) tweaking fix to lock m_disconnect_cb consistently, also writing more commit documentation and adding more code comments to explain the fixes

Fix UndefinedBehaviorSanitizer: null-pointer-use error binding a null pointer
to an unused reference variable. This error should not cause problems in
practice because the reference is not used, but is technically undefined
behavior. Issue was reported:

- Sjors/bitcoin#90 (comment)
- https://github.com/Sjors/bitcoin/actions/runs/15921104847/job/44907875067?pr=90
- Sjors/bitcoin#90 (comment)
- https://github.com/Sjors/bitcoin/actions/runs/15942739184/job/44973004692?pr=90

and fixed:

- Sjors/bitcoin#90 (comment)

Error looks like:

[ TEST ] test.cpp:197: Call IPC method after client connection is closed
/home/runner/work/_temp/src/ipc/libmultiprocess/include/mp/proxy-types.h:612:40: runtime error: reference binding to null pointer of type 'Connection'
    #0 0x5647ad32fc50 in void mp::clientInvoke<mp::ProxyClient<mp::test::messages::FooInterface>, capnp::Request<mp::test::messages::FooInterface::AddParams, mp::test::messages::FooInterface::AddResults> (mp::test::messages::FooInterface::Client::*)(kj::Maybe<capnp::MessageSize>), mp::ClientParam<mp::Accessor<mp::foo_fields::A, 1>, int>, mp::ClientParam<mp::Accessor<mp::foo_fields::B, 1>, int>, mp::ClientParam<mp::Accessor<mp::foo_fields::Result, 2>, int&>>(mp::ProxyClient<mp::test::messages::FooInterface>&, capnp::Request<mp::test::messages::FooInterface::AddParams, mp::test::messages::FooInterface::AddResults> (mp::test::messages::FooInterface::Client::* const&)(kj::Maybe<capnp::MessageSize>), mp::ClientParam<mp::Accessor<mp::foo_fields::A, 1>, int>&&, mp::ClientParam<mp::Accessor<mp::foo_fields::B, 1>, int>&&, mp::ClientParam<mp::Accessor<mp::foo_fields::Result, 2>, int&>&&) /home/runner/work/_temp/build-asan/src/ipc/libmultiprocess/test/./ipc/libmultiprocess/include/mp/proxy-types.h:612:25
    bitcoin-core#1 0x5647ad32b62a in mp::ProxyClient<mp::test::messages::FooInterface>::add(int, int) /home/runner/work/_temp/build-asan/src/ipc/libmultiprocess/test/mp/test/foo.capnp.proxy-client.c++:20:5
    bitcoin-core#2 0x5647ad2eb9ef in mp::test::TestCase197::run() /home/runner/work/_temp/build-asan/src/ipc/libmultiprocess/test/./ipc/libmultiprocess/test/mp/test/test.cpp:206:14
    bitcoin-core#3 0x7f6aaf7100e1  (/lib/x86_64-linux-gnu/libkj-test-1.0.1.so+0x50e1) (BuildId: 2ff7f524274168e50347a2d6dd423f273ae8d268)
    bitcoin-core#4 0x7f6aaf710657  (/lib/x86_64-linux-gnu/libkj-test-1.0.1.so+0x5657) (BuildId: 2ff7f524274168e50347a2d6dd423f273ae8d268)
    bitcoin-core#5 0x7f6aaf49f37f in kj::MainBuilder::MainImpl::operator()(kj::StringPtr, kj::ArrayPtr<kj::StringPtr const>) (/lib/x86_64-linux-gnu/libkj-1.0.1.so+0x5537f) (BuildId: 4b52c0e2756bcb53e58e705fcb10bab8f63f24fd)
    bitcoin-core#6 0x7f6aaf49a499 in kj::runMainAndExit(kj::ProcessContext&, kj::Function<void (kj::StringPtr, kj::ArrayPtr<kj::StringPtr const>)>&&, int, char**) (/lib/x86_64-linux-gnu/libkj-1.0.1.so+0x50499) (BuildId: 4b52c0e2756bcb53e58e705fcb10bab8f63f24fd)
    bitcoin-core#7 0x7f6aaf70efcb in main (/lib/x86_64-linux-gnu/libkj-test-1.0.1.so+0x3fcb) (BuildId: 2ff7f524274168e50347a2d6dd423f273ae8d268)
    bitcoin-core#8 0x7f6aaeeb81c9  (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 42c84c92e6f98126b3e2230ebfdead22c235b667)
    bitcoin-core#9 0x7f6aaeeb828a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 42c84c92e6f98126b3e2230ebfdead22c235b667)
    bitcoin-core#10 0x5647ad1fd614 in _start (/home/runner/work/_temp/build-asan/src/ipc/libmultiprocess/test/mptest+0x11d614) (BuildId: a172f78701ced3f93ad52c3562f181811a1c98e8)

SUMMARY: UndefinedBehaviorSanitizer: null-pointer-use /home/runner/work/_temp/src/ipc/libmultiprocess/include/mp/proxy-types.h:612:40
This reverts commit 196e6fc from
bitcoin-core#118 which added a
workaround which is no longer needed after
315ff53 from
bitcoin-core#160

When the workaround was introduced it prevented segfaults that happened when an
IPC client disconnected during a long-running asynchronous call. But it
prevented them by leaking server objects, and these leaks now sometimes cause
the new "disconnecting and blocking" unit test introduced in bitcoin-core#160 to hang.
Since the workaround is no longer necessary, revert it now to fix the test
hangs.

The problem with test hangs was reported:

- Sjors/bitcoin#90 (comment)
- https://github.com/Sjors/bitcoin/actions/runs/15966265407/job/45027248310?pr=90
- Sjors/bitcoin#90 (comment)
- https://cirrus-ci.com/task/4999408900636672

And the fix was posted:

- Sjors/bitcoin#90 (comment)
@ryanofsky ryanofsky changed the title Fix threadsanitizer errors and add tsan CI job Fix mptest failures in bitcoin CI Jun 30, 2025
Possible typos and grammar issues:

- “try unregister this callback” -> “try to unregister this callback” [missing “to” for infinitive]

bitcoin-core#186 (comment)
@ryanofsky ryanofsky mentioned this pull request Jul 1, 2025
ryanofsky added a commit to ryanofsky/libmultiprocess that referenced this pull request Jul 1, 2025
Possible typos and grammar issues:

- “try unregister this callback” -> “try to unregister this callback” [missing “to” for infinitive]

bitcoin-core#186 (comment)
@ryanofsky
Copy link
Collaborator Author

ryanofsky commented Jul 1, 2025

Updated 71d3107 -> 725beea (pr/tsan.4 -> pr/tsan.5, compare) adding fixes for more errors reported Sjors/bitcoin#90

Will mark this ready for review. There are still followups I would like to make but they can be added separately:

  • Adding an MemorySanitizer job (Add msan CI job #188)
  • Enabling UndefinedBehaviorSanitizer in the sanitizers job. This is a trivial change but it seems to have an interaction with ThreadSanitizer causing new tsan errors I don't understand yet.
  • Adding a dedicated unit test to detect the 7455fc0 hang. The existing "disconnecting and blocking" unit test sometimes catches it but usually doesn't.

@ryanofsky ryanofsky marked this pull request as ready for review July 1, 2025 02:50
Copy link
Member

@Sjors Sjors left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK 725beea

I understand the race condition concerns that this PR addresses, though not always the exact solution - but it's well documented.

@@ -75,6 +75,8 @@ struct ProxyClient<Thread> : public ProxyClientBase<Thread, ::capnp::Void>
//! map. It will also reset m_disconnect_cb so the destructor does not
//! access it. In the normal case where there is no sudden disconnect, the
//! destructor will unregister m_disconnect_cb so the callback is never run.
//! Since this variable is accessed from multiple threads accesses should be
//! guarded with the associated Waiter::m_mutex.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

8218a1d: is there some way to annotate this requirement so the compiler complains? (for a followup)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

re: #186 (comment)

8218a1d: is there some way to annotate this requirement so the compiler complains? (for a followup)

I'll look into it for a followup, hopefully it's possible. The problem I've had doing this is that many times the objects protected by mutexes don't have any way of accessing the mutex variables and referring to them in GUARDED_BY(x) annotations.

But after these fixes, I would like to go through the code and add thread safety annotations and asserts more systematically to enforce that synchronization is applied where needed.

Copy link
Collaborator Author

@ryanofsky ryanofsky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review! Will make a few updates here to address comments

@@ -75,6 +75,8 @@ struct ProxyClient<Thread> : public ProxyClientBase<Thread, ::capnp::Void>
//! map. It will also reset m_disconnect_cb so the destructor does not
//! access it. In the normal case where there is no sudden disconnect, the
//! destructor will unregister m_disconnect_cb so the callback is never run.
//! Since this variable is accessed from multiple threads accesses should be
//! guarded with the associated Waiter::m_mutex.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

re: #186 (comment)

8218a1d: is there some way to annotate this requirement so the compiler complains? (for a followup)

I'll look into it for a followup, hopefully it's possible. The problem I've had doing this is that many times the objects protected by mutexes don't have any way of accessing the mutex variables and referring to them in GUARDED_BY(x) annotations.

But after these fixes, I would like to go through the code and add thread safety annotations and asserts more systematically to enforce that synchronization is applied where needed.

Copy link
Collaborator Author

@ryanofsky ryanofsky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated 725beea -> 6f340a5 (pr/tsan.5 -> pr/tsan.6, compare) just improving a few comments in response to review questions

@Sjors
Copy link
Member

Sjors commented Jul 1, 2025

re-ACK 6f340a5

@ryanofsky
Copy link
Collaborator Author

Thanks for the reack! I will go ahead and merge this to unblock bitcoin/bitcoin#32345 and get it out of draft.

I'm working on the followups mentioned #186 (comment) (writing a unit test to cover the c6f7fdf fix and adding msan and ubsan jobs). Adding thread safety annotations suggested #186 (comment) is another possible followup.

Trying to write a new test for c6f7fdf made me realize the existing "disconnecting and blocking" unit test is inherently racy, and it will usually test the ProxyServer object being destroyed before the server Connection object, but sometimes tests the opposite, and it's not easily possible to modify the test to guarantee it tests the first one. The test also isn't covering the case charlatan suggested where the server tries to shut itself down during an IPC call #160 (comment). So I'm trying to rewrite that test using promises instead of threads to cover all 3 cases deterministically instead of 1 case randomly.

@ryanofsky ryanofsky merged commit a11e690 into bitcoin-core:master Jul 1, 2025
4 checks passed
ryanofsky added a commit to ryanofsky/bitcoin that referenced this pull request Jul 1, 2025
…05c238

a11e6905c238 Merge bitcoin-core/libmultiprocess#186: Fix mptest failures in bitcoin CI
6f340a583f2b doc: fix DrahtBot LLM Linter error
c6f7fdf17350 type-context: revert client disconnect workaround
e09143d2ea2f proxy-types: fix UndefinedBehaviorSanitizer: null-pointer-use
84b292fcc4db mptest: fix MemorySanitizer: use-of-uninitialized-value
fe4a188803c6 proxy-io: fix race conditions in disconnect callback code
d8011c83608e proxy-io: fix race conditions in ProxyClientBase cleanup handler
97e82ce19c47 doc: Add note about Waiter::m_mutex and interaction with the EventLoop::m_mutex
81d58f5580e8 refactor: Rename ProxyClient cleanup_it variable
07230f259f55 refactor: rename ProxyClient<Thread>::m_cleanup_it
c0efaa5e8cb1 Merge bitcoin-core/libmultiprocess#187: ci: have bash scripts explicitly opt out of locale dependence.
0d986ff144cd mptest: fix race condition in TestSetup constructor
d2f6aa2e84ef ci: add thread sanitizer job
3a6db38e561f ci: rename configs to .bash
401e0ce1d9c3 ci: add copyright to bash scripts
e956467ae464 ci: export LC_ALL
8954cc0377d8 Merge bitcoin-core/libmultiprocess#184: Add CI jobs and fix clang-tidy and iwyu errors
757e13a75546 ci: add gnu32 cross-compiled 32-bit build
15bf349000eb doc: fix typo found by DrahtBot
1a598d5905f7 clang-tidy: drop 'bitcoin-*' check
cbb1e43fdc6e ci: test libc++ instead of libstdc++ in one job
76313450c2c4 type-context: disable clang-tidy UndefinedBinaryOperatorResult error
4896e7fe51ba proxy-types: fix clang-tidy EnumCastOutOfRange error
060a73926956 proxy-types: fix clang-tidy StackAddressEscape error
977d721020f6 ci: add github actions jobs testing gcc, clang-20, clang-tidy, and iwyu
0d5f1faae5da iwyu: fix add/remove include errors
753d2b10cc27 util: fix clang-tidy modernize-use-equals-default error
ae4f1dc2bb1a type-number: fix clang-tidy modernize-use-nullptr error
07a741bf6946 proxy-types: fix clang-tidy bugprone-use-after-move error
3673114bc9d9 proxy-types: fix clang-tidy bugprone-use-after-move error
422923f38485 proxy-types: fix clang-tidy bugprone-use-after-move error
c6784c6adefa mpgen: disable clang-tidy misc-no-recursion error
c5498aa11ba6 tidy: copy clang-tidy file from bitcoin core
258a617c1eec Merge bitcoin-core/libmultiprocess#160: refactor: EventLoop locking cleanups + client disconnect exception
84cf56a0b5f4 test: Test disconnects during IPC calls
949573da8411 Prevent IPC server crash if disconnected during IPC call
019839758085 Merge bitcoin-core/libmultiprocess#179: scripted-diff: Remove copyright year (ranges)
ea38392960e1 Prevent EventLoop async cleanup thread early exit during shutdown
616d9a75d20a doc: Document ProxyClientBase destroy_connection option
56fff76f940b Improve IPC client disconnected exceptions
9b8ed3dc5f87 refactor: Add clang thread safety annotations to EventLoop
52256e730f51 refactor: Remove DestructorCatcher and AsyncCallable
f24894794adf refactor: Drop addClient/removeClient methods
2b830e558e61 refactor: Use EventLoopRef instead of addClient/removeClient
315ff537fb65 refactor: Add ProxyContext EventLoop* member
9aaeec3678d3 proxy-io.h: Add EventLoopRef RAII class handle addClient/removeClient refcounting
f58c8d8ba2f0 proxy-io.h: Add more detailed EventLoop comment
5108445e5d16 test: Add test coverage for client & server disconnections
59030c68cb5f Merge bitcoin-core/libmultiprocess#181: type-function.h: Fix CustomBuildField overload
688140b1dffc test: Add coverage for type-function.h
8b96229da58e type-function.h: Fix CustomBuildField overload
fa2ff9a66842 scripted-diff: Remove copyright year (ranges)

git-subtree-dir: src/ipc/libmultiprocess
git-subtree-split: a11e6905c238dc35a8bbef995190296bc6329d49
ryanofsky added a commit to ryanofsky/bitcoin that referenced this pull request Aug 8, 2025
…34bad2

b4120d34bad2 Merge bitcoin-core/libmultiprocess#192: doc: fix typos
6ecbdcd35a93 doc: fix typos
a11e6905c238 Merge bitcoin-core/libmultiprocess#186: Fix mptest failures in bitcoin CI
6f340a583f2b doc: fix DrahtBot LLM Linter error
c6f7fdf17350 type-context: revert client disconnect workaround
e09143d2ea2f proxy-types: fix UndefinedBehaviorSanitizer: null-pointer-use
84b292fcc4db mptest: fix MemorySanitizer: use-of-uninitialized-value
fe4a188803c6 proxy-io: fix race conditions in disconnect callback code
d8011c83608e proxy-io: fix race conditions in ProxyClientBase cleanup handler
97e82ce19c47 doc: Add note about Waiter::m_mutex and interaction with the EventLoop::m_mutex
81d58f5580e8 refactor: Rename ProxyClient cleanup_it variable
07230f259f55 refactor: rename ProxyClient<Thread>::m_cleanup_it
c0efaa5e8cb1 Merge bitcoin-core/libmultiprocess#187: ci: have bash scripts explicitly opt out of locale dependence.
0d986ff144cd mptest: fix race condition in TestSetup constructor
d2f6aa2e84ef ci: add thread sanitizer job
3a6db38e561f ci: rename configs to .bash
401e0ce1d9c3 ci: add copyright to bash scripts
e956467ae464 ci: export LC_ALL
8954cc0377d8 Merge bitcoin-core/libmultiprocess#184: Add CI jobs and fix clang-tidy and iwyu errors
757e13a75546 ci: add gnu32 cross-compiled 32-bit build
15bf349000eb doc: fix typo found by DrahtBot
1a598d5905f7 clang-tidy: drop 'bitcoin-*' check
cbb1e43fdc6e ci: test libc++ instead of libstdc++ in one job
76313450c2c4 type-context: disable clang-tidy UndefinedBinaryOperatorResult error
4896e7fe51ba proxy-types: fix clang-tidy EnumCastOutOfRange error
060a73926956 proxy-types: fix clang-tidy StackAddressEscape error
977d721020f6 ci: add github actions jobs testing gcc, clang-20, clang-tidy, and iwyu
0d5f1faae5da iwyu: fix add/remove include errors
753d2b10cc27 util: fix clang-tidy modernize-use-equals-default error
ae4f1dc2bb1a type-number: fix clang-tidy modernize-use-nullptr error
07a741bf6946 proxy-types: fix clang-tidy bugprone-use-after-move error
3673114bc9d9 proxy-types: fix clang-tidy bugprone-use-after-move error
422923f38485 proxy-types: fix clang-tidy bugprone-use-after-move error
c6784c6adefa mpgen: disable clang-tidy misc-no-recursion error
c5498aa11ba6 tidy: copy clang-tidy file from bitcoin core
258a617c1eec Merge bitcoin-core/libmultiprocess#160: refactor: EventLoop locking cleanups + client disconnect exception
84cf56a0b5f4 test: Test disconnects during IPC calls
949573da8411 Prevent IPC server crash if disconnected during IPC call
019839758085 Merge bitcoin-core/libmultiprocess#179: scripted-diff: Remove copyright year (ranges)
ea38392960e1 Prevent EventLoop async cleanup thread early exit during shutdown
616d9a75d20a doc: Document ProxyClientBase destroy_connection option
56fff76f940b Improve IPC client disconnected exceptions
9b8ed3dc5f87 refactor: Add clang thread safety annotations to EventLoop
52256e730f51 refactor: Remove DestructorCatcher and AsyncCallable
f24894794adf refactor: Drop addClient/removeClient methods
2b830e558e61 refactor: Use EventLoopRef instead of addClient/removeClient
315ff537fb65 refactor: Add ProxyContext EventLoop* member
9aaeec3678d3 proxy-io.h: Add EventLoopRef RAII class handle addClient/removeClient refcounting
f58c8d8ba2f0 proxy-io.h: Add more detailed EventLoop comment
5108445e5d16 test: Add test coverage for client & server disconnections
59030c68cb5f Merge bitcoin-core/libmultiprocess#181: type-function.h: Fix CustomBuildField overload
688140b1dffc test: Add coverage for type-function.h
8b96229da58e type-function.h: Fix CustomBuildField overload
fa2ff9a66842 scripted-diff: Remove copyright year (ranges)

git-subtree-dir: src/ipc/libmultiprocess
git-subtree-split: b4120d34bad2de28141c5770f6e8df8e54898987
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants