Avoid errors from asynchronous (non-c++) clients #240

pull ryanofsky wants to merge 4 commits into bitcoin-core:master from ryanofsky:pr/promise changing 10 files +223 −65
  1. ryanofsky commented at 4:06 am on January 21, 2026: collaborator
    The PR avoids errors from non-C++ rust & python clients when they make asynchronous requests (https://github.com/bitcoin/bitcoin/issues/33923) and unclean disconnects (https://github.com/bitcoin/bitcoin/issues/34250). Neither of these errors are easily possible to trigger from libmultiprocess clients because of its blocking interface and RAII semantics, but they are fairly easy to trigger from rust and python and there is a test triggering both of them in https://github.com/bitcoin/bitcoin/pull/34284
  2. DrahtBot commented at 4:07 am on January 21, 2026: none

    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
    Concept ACK ismaelsadeeq

    If your review is incorrectly listed, please copy-paste <!–meta-tag:bot-skip–> into the comment that the bot should ignore.

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #218 (Better error and log messages by ryanofsky)

    If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

    LLM Linter (✨ experimental)

    Possible typos and grammar issues:

    • is is -> is [duplication: “is” is repeated (“m_thread_ready is is signaled”)]
    • letting parameters to be deleted -> letting parameters be deleted [incorrect infinitive usage; use “be deleted” instead of “to be deleted”]

    2026-02-11 20:07:08

  3. ryanofsky force-pushed on Jan 22, 2026
  4. ryanofsky commented at 2:40 am on January 22, 2026: collaborator
    Updated ba865d9d7bffc391f66566ccea18f0a2b6be84e0 -> 92a2c5f66282c9f8e283366be221790056c80d22 (pr/promise.1 -> pr/promise.2, compare) fixing various ci errors (iwyu, compiler errors) https://github.com/bitcoin-core/libmultiprocess/actions/runs/21196838571
  5. refactor: Add ProxyServer<Thread>::post() method
    The change moves code responsible for posting work to the execution thread, and
    notifying the event loop thread after execution is finished out of
    type-context.h and into an new ProxyServer<Thread>::post() method.
    
    This commit does not change behavior, but improvements are made in upcoming
    commits which extend the new method.
    558ae11d8f
  6. Allow simultaneous calls on same Context.thread
    If multiple IPC requests happen at the same time specifying same Context.thread
    to run the requests on, queue the requests to execute in the order they are
    received instead of raising a "thread busy" exception.
    
    This change has no effect on C++ clients using libmultiprocess as a client
    library, since the libmultiprocess client only makes blocking calls and creates
    a server thread for every client thread, so it's not possible for there to be
    multiple calls on the same server thread.
    
    But this change may be useful for rust and python clients as discussed
    https://github.com/bitcoin/bitcoin/issues/33923
    92fed35b0b
  7. Prevent crash on unclean disconnect if abandoned IPC call returns interface pointer
    This is a potential fix for https://github.com/bitcoin/bitcoin/issues/34250
    which reports that bitcoin node crashes if a rust stratrumv2 mining client
    calls BlockTemplate.waitNext() and disconnects without waiting for a response
    from the call, and if mempool fees increased so the call returns a non-null
    interface BlockTemplate pointer.
    
    The node would crash in this case while trying to call MakeProxyServer on the
    returned BlockTemplate pointer, which would fail because MakeProxyServer would
    try to use a reference to the Connection object that had been deleted as a as a
    result of the disconnect.
    
    The fix works by:
    
    - Adding a Connection::m_canceler member variable and using it to cancel any
      IPC response promises that are pending when the connection is destroyed.
    
    - Updating type-context.h PassField() function to use promise.attach() as
      described https://capnproto.org/cxxrpc.html#cancellation to detect
      cancellation and set a ServerContext::m_cancelled variable.
    
    - Updating ServerCall to check the ServerContext::m_cancelled status after any
      C++ server method returns, and throw an exception if it is set.
    
    - Updating type-context.h PassField() function to deal with the exception
      by catching and logging it, and to deal with cancelled status by not
      trying to fulfill the cancelled promise.
    0a224251ec
  8. ismaelsadeeq commented at 10:21 am on January 27, 2026: member
    Concept ACK
  9. ryanofsky force-pushed on Jan 27, 2026
  10. ryanofsky commented at 7:05 pm on January 27, 2026: collaborator
    Updated 92a2c5f66282c9f8e283366be221790056c80d22 -> 1f307fb8fd1aff4a2e97b8f179b4162049f7af50 (pr/promise.2 -> pr/promise.3, compare) fixing tsan error caused by deleting ready_fulfiller promise fulfiller outside the event loop thread https://github.com/bitcoin-core/libmultiprocess/actions/runs/21233968144/job/61097851891?pr=240
  11. ryanofsky commented at 4:31 am on January 28, 2026: collaborator

    Have been debugging a TSAN race condition (https://github.com/bitcoin/bitcoin/actions/runs/21421194207/job/61680781122?pr=34422#step:11:4303) the functional test in https://github.com/bitcoin/bitcoin/pull/34422 detects in the new code, that is not triggered here because this PR does not currently include a test that triggers IPC request cancellation.

    The race seems real and fixable, also probably unlikely to cause problems in practice. In the control flow when an IPC gets cancelled, TSAN sees the worker thread doing a CallContext::getParams call before the cancellation, triggering a read of the RpcCallContext::request pointer. Then after the cancellation occurs, the I/O thread wakes up and destroys the CallContext object, triggering a write of RpcCallContext::request pointer. This is not actually dangerous because the write always happens before the read, but TSAN is unable to detect any “happens-before” relationship between the read and write events, so considers this to be a race.

    I think this should be fixable by having the worker thread acquire a lock in the brief period when it reads request parameters, before executing the IPC call, and also acquire the lock after executing the call when it is writing request results. Then the cancellation callback, if triggered, can acquire the same lock and TSAN can be aware of the ordering between the two threads

  12. Sjors commented at 2:17 pm on February 10, 2026: member

    should be fixable by having the worker thread acquire a lock in the brief period … @ryanofsky is that something you want to add here?

  13. Fix getParams() delete race on async cancellations
    TSAN reports errors when run_unclean_disconnect_test from
    https://github.com/bitcoin/bitcoin/pull/34284 is run because when the test
    disconnects during a `waitNext` IPC call there is no thread synchronization
    preventing the `waitNext` parameters from being deleted while they are being
    accessed.
    
    Fix by introducing a mutex to protect against this race. TSAN output from the
    test is shown below.
    
    WARNING: ThreadSanitizer: data race (pid=86917)
      Write of size 8 at 0x72300001e380 by thread T2:
        #0 operator delete(void*) <null> (bitcoin-node+0x1c1196)
        #1 capnp::BufferedMessageStream::MessageReaderImpl::~MessageReaderImpl() <null> (libcapnp-rpc.so.1.2.0+0x6095e) (BuildId: 98f819a103310afbd8f791a0c80d203a4d1b9791)
        #2 mp::EventLoop::loop() /home/src/bitcoin/build/src/ipc/libmultiprocess/./ipc/libmultiprocess/src/mp/proxy.cpp:244:68 (bitcoin-node+0x17b135e)
        #3 ipc::capnp::(anonymous namespace)::CapnpProtocol::startLoop(char const*)::'lambda'()::operator()() const /home/src/bitcoin/build/src/ipc/./ipc/capnp/protocol.cpp:136:21 (bitcoin-node+0xe516e4)
        #4 void std::__invoke_impl<void, ipc::capnp::(anonymous namespace)::CapnpProtocol::startLoop(char const*)::'lambda'()>(std::__invoke_other, ipc::capnp::(anonymous namespace)::CapnpProtocol::startLoop(char const*)::'lambda'()&&) /nix/store/kzq78n13l8w24jn8bx4djj79k5j717f1-gcc-14.3.0/include/c++/14.3.0/bits/invoke.h:61:14 (bitcoin-node+0xe514a2)
        #5 std::__invoke_result<ipc::capnp::(anonymous namespace)::CapnpProtocol::startLoop(char const*)::'lambda'()>::type std::__invoke<ipc::capnp::(anonymous namespace)::CapnpProtocol::startLoop(char const*)::'lambda'()>(ipc::capnp::(anonymous namespace)::CapnpProtocol::startLoop(char const*)::'lambda'()&&) /nix/store/kzq78n13l8w24jn8bx4djj79k5j717f1-gcc-14.3.0/include/c++/14.3.0/bits/invoke.h:96:14 (bitcoin-node+0xe513a2)
        #6 void std::thread::_Invoker<std::tuple<ipc::capnp::(anonymous namespace)::CapnpProtocol::startLoop(char const*)::'lambda'()>>::_M_invoke<0ul>(std::_Index_tuple<0ul>) /nix/store/kzq78n13l8w24jn8bx4djj79k5j717f1-gcc-14.3.0/include/c++/14.3.0/bits/std_thread.h:301:13 (bitcoin-node+0xe5130a)
        #7 std::thread::_Invoker<std::tuple<ipc::capnp::(anonymous namespace)::CapnpProtocol::startLoop(char const*)::'lambda'()>>::operator()() /nix/store/kzq78n13l8w24jn8bx4djj79k5j717f1-gcc-14.3.0/include/c++/14.3.0/bits/std_thread.h:308:11 (bitcoin-node+0xe51272)
        #8 std::thread::_State_impl<std::thread::_Invoker<std::tuple<ipc::capnp::(anonymous namespace)::CapnpProtocol::startLoop(char const*)::'lambda'()>>>::_M_run() /nix/store/kzq78n13l8w24jn8bx4djj79k5j717f1-gcc-14.3.0/include/c++/14.3.0/bits/std_thread.h:253:13 (bitcoin-node+0xe5106a)
        #9 execute_native_thread_routine <null> (libstdc++.so.6+0xed063)
    
      Previous read of size 8 at 0x72300001e380 by thread T13:
        #0 capnp::_::DirectWireValue<double>::get() const /nix/store/8xjrdb1y9ng6w6yknzg9h6fkxd3sb824-capnproto-1.2.0/include/capnp/endian.h:77:44 (bitcoin-node+0x10dd19a)
        #1 double capnp::_::StructReader::getDataField<double>(unsigned int) const /nix/store/8xjrdb1y9ng6w6yknzg9h6fkxd3sb824-capnproto-1.2.0/include/capnp/layout.h:1100:84 (bitcoin-node+0x10dd0d1)
        #2 ipc::capnp::messages::BlockWaitOptions::Reader::getTimeout() const /home/src/bitcoin/build/src/ipc/capnp/mining.capnp.h:6092:18 (bitcoin-node+0x1222b65)
        #3 decltype(fp.getTimeout()) mp::mining_fields::Timeout::get<ipc::capnp::messages::BlockWaitOptions::Reader const&>(ipc::capnp::messages::BlockWaitOptions::Reader const&) /home/src/bitcoin/build/src/ipc/capnp/mining.capnp.proxy.h:163:88 (bitcoin-node+0x1222ab6)
        #4 mp::StructField<mp::Accessor<mp::mining_fields::Timeout, 3>, ipc::capnp::messages::BlockWaitOptions::Reader const>::get() const /home/src/bitcoin/build/src/ipc/./ipc/libmultiprocess/include/mp/proxy-types.h:40:41 (bitcoin-node+0x1222a16)
        #5 decltype(auto) mp::CustomReadField<double, std::ratio<1l, 1000l>, mp::StructField<mp::Accessor<mp::mining_fields::Timeout, 3>, ipc::capnp::messages::BlockWaitOptions::Reader const>, mp::ReadDestUpdate<std::chrono::duration<double, std::ratio<1l, 1000l>>>>(mp::TypeList<std::chrono::duration<double, std::ratio<1l, 1000l>>>, mp::Priority<1>, mp::InvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Timeout, 3>, ipc::capnp::messages::BlockWaitOptions::Reader const>&&, mp::ReadDestUpdate<std::chrono::duration<double, std::ratio<1l, 1000l>>>&&) /home/src/bitcoin/build/src/ipc/./ipc/libmultiprocess/include/mp/type-chrono.h:30:38 (bitcoin-node+0x1222899)
        #6 decltype(auto) mp::ReadField<std::chrono::duration<double, std::ratio<1l, 1000l>>, mp::InvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Timeout, 3>, ipc::capnp::messages::BlockWaitOptions::Reader const>, mp::ReadDestUpdate<std::chrono::duration<double, std::ratio<1l, 1000l>>>>(mp::TypeList<std::chrono::duration<double, std::ratio<1l, 1000l>>>, mp::InvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Timeout, 3>, ipc::capnp::messages::BlockWaitOptions::Reader const>&&, mp::ReadDestUpdate<std::chrono::duration<double, std::ratio<1l, 1000l>>>&&) /home/src/bitcoin/build/src/ipc/./ipc/libmultiprocess/include/mp/proxy-types.h:172:12 (bitcoin-node+0x12222d6)
        #7 void mp::ReadOne<0ul, node::BlockWaitOptions, mp::StructField<mp::Accessor<mp::mining_fields::Options, 17>, ipc::capnp::messages::BlockTemplate::WaitNextParams::Reader const>&, node::BlockWaitOptions&>(mp::TypeList<node::BlockWaitOptions>, mp::InvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Options, 17>, ipc::capnp::messages::BlockTemplate::WaitNextParams::Reader const>&, node::BlockWaitOptions&, std::enable_if<0ul != ProxyType<node::BlockWaitOptions>::fields, void>::type*) /home/src/bitcoin/build/src/ipc/./ipc/libmultiprocess/include/mp/type-struct.h:59:5 (bitcoin-node+0x122213a)
        #8 auto decltype(auto) mp::CustomReadField<node::BlockWaitOptions, mp::StructField<mp::Accessor<mp::mining_fields::Options, 17>, ipc::capnp::messages::BlockTemplate::WaitNextParams::Reader const>, mp::ReadDestEmplace<node::BlockWaitOptions, void mp::PassField<mp::Accessor<mp::mining_fields::Options, 17>, node::BlockWaitOptions, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>>(mp::Priority<0>, mp::TypeList<node::BlockWaitOptions>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(auto&&...)>>(mp::TypeList<node::BlockWaitOptions>, mp::Priority<1>, mp::InvokeContext&, node::BlockWaitOptions&&, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&&, mp::ProxyType<node::BlockWaitOptions>::Struct*)::'lambda'(node::BlockWaitOptions&)::operator()<node::BlockWaitOptions>(node::BlockWaitOptions&) const /home/src/bitcoin/build/src/ipc/./ipc/libmultiprocess/include/mp/type-struct.h:81:48 (bitcoin-node+0x1221a71)
        #9 decltype(auto) mp::ReadDestEmplace<node::BlockWaitOptions, void mp::PassField<mp::Accessor<mp::mining_fields::Options, 17>, node::BlockWaitOptions, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>>(mp::Priority<0>, mp::TypeList<node::BlockWaitOptions>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(auto&&...)>::update<decltype(auto) mp::CustomReadField<node::BlockWaitOptions, mp::StructField<mp::Accessor<mp::mining_fields::Options, 17>, ipc::capnp::messages::BlockTemplate::WaitNextParams::Reader const>, mp::ReadDestEmplace<node::BlockWaitOptions, void mp::PassField<mp::Accessor<mp::mining_fields::Options, 17>, node::BlockWaitOptions, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>>(mp::Priority<0>, mp::TypeList<node::BlockWaitOptions>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(auto&&...)>>(mp::TypeList<node::BlockWaitOptions>, mp::Priority<1>, mp::InvokeContext&, node::BlockWaitOptions&&, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&&, mp::ProxyType<node::BlockWaitOptions>::Struct*)::'lambda'(node::BlockWaitOptions&)>(node::BlockWaitOptions&&) /home/src/bitcoin/build/src/ipc/./ipc/libmultiprocess/include/mp/proxy-types.h:122:13 (bitcoin-node+0x122190d)
        #10 decltype(auto) mp::CustomReadField<node::BlockWaitOptions, mp::StructField<mp::Accessor<mp::mining_fields::Options, 17>, ipc::capnp::messages::BlockTemplate::WaitNextParams::Reader const>, mp::ReadDestEmplace<node::BlockWaitOptions, void mp::PassField<mp::Accessor<mp::mining_fields::Options, 17>, node::BlockWaitOptions, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>>(mp::Priority<0>, mp::TypeList<node::BlockWaitOptions>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(auto&&...)>>(mp::TypeList<node::BlockWaitOptions>, mp::Priority<1>, mp::InvokeContext&, node::BlockWaitOptions&&, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&&, mp::ProxyType<node::BlockWaitOptions>::Struct*) /home/src/bitcoin/build/src/ipc/./ipc/libmultiprocess/include/mp/type-struct.h:81:22 (bitcoin-node+0x1221832)
        #11 decltype(auto) mp::ReadField<node::BlockWaitOptions, mp::InvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Options, 17>, ipc::capnp::messages::BlockTemplate::WaitNextParams::Reader const>, mp::ReadDestEmplace<node::BlockWaitOptions, void mp::PassField<mp::Accessor<mp::mining_fields::Options, 17>, node::BlockWaitOptions, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>>(mp::Priority<0>, mp::TypeList<node::BlockWaitOptions>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(auto&&...)>>(mp::TypeList<auto...>, mp::InvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Options, 17>, ipc::capnp::messages::BlockTemplate::WaitNextParams::Reader const>&&, mp::ReadDestEmplace<node::BlockWaitOptions, void mp::PassField<mp::Accessor<mp::mining_fields::Options, 17>, node::BlockWaitOptions, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>>(mp::Priority<0>, mp::TypeList<node::BlockWaitOptions>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(auto&&...)>&&) /home/src/bitcoin/build/src/ipc/./ipc/libmultiprocess/include/mp/proxy-types.h:172:12 (bitcoin-node+0x122170a)
        #12 void mp::MaybeReadField<mp::TypeList<node::BlockWaitOptions>, mp::InvokeContext&, mp::StructField<mp::Accessor<mp::mining_fields::Options, 17>, ipc::capnp::messages::BlockTemplate::WaitNextParams::Reader const>, mp::ReadDestEmplace<node::BlockWaitOptions, void mp::PassField<mp::Accessor<mp::mining_fields::Options, 17>, node::BlockWaitOptions, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>>(mp::Priority<0>, mp::TypeList<node::BlockWaitOptions>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&)::'lambda'(auto&&...)>>(std::integral_constant<bool, true>, auto&&...) /home/src/bitcoin/build/src/ipc/./ipc/libmultiprocess/include/mp/proxy-types.h:269:5 (bitcoin-node+0x1220d52)
        #13 void mp::PassField<mp::Accessor<mp::mining_fields::Options, 17>, node::BlockWaitOptions, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>>(mp::Priority<0>, mp::TypeList<node::BlockWaitOptions>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&) /home/src/bitcoin/build/src/ipc/./ipc/libmultiprocess/include/mp/proxy-types.h:297:5 (bitcoin-node+0x1220a89)
        #14 decltype(auto) mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>::invoke<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>, mp::TypeList<node::BlockWaitOptions>>(mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&, mp::TypeList<node::BlockWaitOptions>) const /home/src/bitcoin/build/src/ipc/./ipc/libmultiprocess/include/mp/proxy-types.h:549:16 (bitcoin-node+0x12208ea)
        #15 std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<node::BlockWaitOptions>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<node::BlockWaitOptions>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda1'()::operator()() const /home/src/bitcoin/build/src/ipc/./ipc/libmultiprocess/include/mp/type-context.h:138:21 (bitcoin-node+0x1220534)
        #16 kj::Maybe<kj::Exception> kj::runCatchingExceptions<std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<node::BlockWaitOptions>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<node::BlockWaitOptions>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda1'()>(mp::Accessor<mp::mining_fields::Context, 17>&&) /nix/store/8xjrdb1y9ng6w6yknzg9h6fkxd3sb824-capnproto-1.2.0/include/kj/exception.h:371:5 (bitcoin-node+0x121d6a1)
        #17 std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<node::BlockWaitOptions>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<node::BlockWaitOptions>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&) /home/src/bitcoin/build/src/ipc/./ipc/libmultiprocess/include/mp/type-context.h:138:21 (bitcoin-node+0x121cc6c)
        #18 kj::Promise<mp::Accessor<mp::mining_fields::Context, 17>> mp::ProxyServer<mp::Thread>::post<capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>, std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<node::BlockWaitOptions>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<node::BlockWaitOptions>&&)::'lambda'(mp::CancelMonitor&)>(mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&&)::'lambda'()::operator()()::'lambda'()::operator()()::'lambda0'()::operator()() const /home/src/bitcoin/build/src/ipc/./ipc/libmultiprocess/include/mp/proxy-io.h:720:100 (bitcoin-node+0x121c4d3)
        #19 kj::Maybe<kj::Exception> kj::runCatchingExceptions<kj::Promise<mp::Accessor<mp::mining_fields::Context, 17>> mp::ProxyServer<mp::Thread>::post<capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>, std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<node::BlockWaitOptions>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<node::BlockWaitOptions>&&)::'lambda'(mp::CancelMonitor&)>(mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&&)::'lambda'()::operator()()::'lambda'()::operator()()::'lambda0'()>(mp::Accessor<mp::mining_fields::Context, 17>&&) /nix/store/8xjrdb1y9ng6w6yknzg9h6fkxd3sb824-capnproto-1.2.0/include/kj/exception.h:371:5 (bitcoin-node+0x121b371)
        #20 kj::Promise<mp::Accessor<mp::mining_fields::Context, 17>> mp::ProxyServer<mp::Thread>::post<capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>, std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<node::BlockWaitOptions>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<node::BlockWaitOptions>&&)::'lambda'(mp::CancelMonitor&)>(mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&&)::'lambda'()::operator()()::'lambda'()::operator()() /home/src/bitcoin/build/src/ipc/./ipc/libmultiprocess/include/mp/proxy-io.h:720:48 (bitcoin-node+0x121aeb2)
        #21 kj::Function<void ()>::Impl<kj::Promise<mp::Accessor<mp::mining_fields::Context, 17>> mp::ProxyServer<mp::Thread>::post<capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>, std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<node::BlockWaitOptions>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<node::BlockWaitOptions>&&)::'lambda'(mp::CancelMonitor&)>(mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&&)::'lambda'()::operator()()::'lambda'()>::operator()() /nix/store/8xjrdb1y9ng6w6yknzg9h6fkxd3sb824-capnproto-1.2.0/include/kj/function.h:142:14 (bitcoin-node+0x121acfa)
        #22 kj::Function<void ()>::operator()() /nix/store/8xjrdb1y9ng6w6yknzg9h6fkxd3sb824-capnproto-1.2.0/include/kj/function.h:119:12 (bitcoin-node+0xe8e221)
        #23 void mp::Unlock<mp::Lock, kj::Function<void ()>&>(mp::Lock&, kj::Function<void ()>&) /home/src/bitcoin/build/src/ipc/./ipc/libmultiprocess/include/mp/util.h:210:5 (bitcoin-node+0xe8dd31)
        #24 void mp::Waiter::wait<mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0::operator()() const::'lambda'()>(mp::Lock&, mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0::operator()() const::'lambda'())::'lambda'()::operator()() const /home/src/bitcoin/build/src/ipc/libmultiprocess/./ipc/libmultiprocess/include/mp/proxy-io.h:371:17 (bitcoin-node+0x17b827f)
        #25 void std::condition_variable::wait<void mp::Waiter::wait<mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0::operator()() const::'lambda'()>(mp::Lock&, mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0::operator()() const::'lambda'())::'lambda'()>(std::unique_lock<std::mutex>&, mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0::operator()() const::'lambda'()) /nix/store/kzq78n13l8w24jn8bx4djj79k5j717f1-gcc-14.3.0/include/c++/14.3.0/condition_variable:104:10 (bitcoin-node+0x17b815e)
        #26 void mp::Waiter::wait<mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0::operator()() const::'lambda'()>(mp::Lock&, mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0::operator()() const::'lambda'()) /home/src/bitcoin/build/src/ipc/libmultiprocess/./ipc/libmultiprocess/include/mp/proxy-io.h:362:14 (bitcoin-node+0x17b7fdf)
        #27 mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0::operator()() const /home/src/bitcoin/build/src/ipc/libmultiprocess/./ipc/libmultiprocess/src/mp/proxy.cpp:423:34 (bitcoin-node+0x17b7e39)
        #28 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/kzq78n13l8w24jn8bx4djj79k5j717f1-gcc-14.3.0/include/c++/14.3.0/bits/invoke.h:61:14 (bitcoin-node+0x17b7ba2)
        #29 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/kzq78n13l8w24jn8bx4djj79k5j717f1-gcc-14.3.0/include/c++/14.3.0/bits/invoke.h:96:14 (bitcoin-node+0x17b7aa2)
        #30 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/kzq78n13l8w24jn8bx4djj79k5j717f1-gcc-14.3.0/include/c++/14.3.0/bits/std_thread.h:301:13 (bitcoin-node+0x17b7a0a)
        #31 std::thread::_Invoker<std::tuple<mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0>>::operator()() /nix/store/kzq78n13l8w24jn8bx4djj79k5j717f1-gcc-14.3.0/include/c++/14.3.0/bits/std_thread.h:308:11 (bitcoin-node+0x17b7972)
        #32 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/kzq78n13l8w24jn8bx4djj79k5j717f1-gcc-14.3.0/include/c++/14.3.0/bits/std_thread.h:253:13 (bitcoin-node+0x17b745a)
        #33 execute_native_thread_routine <null> (libstdc++.so.6+0xed063)
    
      Thread T2 'b-capnp-loop' (tid=86923, running) created by main thread at:
        #0 pthread_create <null> (bitcoin-node+0x13a395)
        #1 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State>>, void (*)()) <null> (libstdc++.so.6+0xed138)
        #2 ipc::capnp::(anonymous namespace)::CapnpProtocol::startLoop(char const*) /home/src/bitcoin/build/src/ipc/./ipc/capnp/protocol.cpp:127:25 (bitcoin-node+0xe50b88)
        #3 ipc::capnp::(anonymous namespace)::CapnpProtocol::listen(int, char const*, interfaces::Init&) /home/src/bitcoin/build/src/ipc/./ipc/capnp/protocol.cpp:87:9 (bitcoin-node+0xe503b2)
        #4 ipc::(anonymous namespace)::IpcImpl::listenAddress(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&) /home/src/bitcoin/build/src/ipc/./ipc/interfaces.cpp:111:21 (bitcoin-node+0xe475c5)
        #5 AppInitMain(node::NodeContext&, interfaces::BlockAndHeaderTipInfo*) /home/src/bitcoin/build/src/./init.cpp:1483:22 (bitcoin-node+0x1f8a67)
        #6 AppInit(node::NodeContext&) /home/src/bitcoin/build/src/./bitcoind.cpp:242:43 (bitcoin-node+0x1c42fd)
        #7 main /home/src/bitcoin/build/src/./bitcoind.cpp:283:10 (bitcoin-node+0x1c2a95)
    
      Thread T13 (tid=86942, running) created by thread T2 at:
        #0 pthread_create <null> (bitcoin-node+0x13a395)
        #1 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State>>, void (*)()) <null> (libstdc++.so.6+0xed138)
        #2 mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>) /home/src/bitcoin/build/src/ipc/libmultiprocess/./ipc/libmultiprocess/src/mp/proxy.cpp:416:17 (bitcoin-node+0x17b3821)
        #3 mp::ThreadMap::Server::dispatchCallInternal(unsigned short, capnp::CallContext<capnp::AnyPointer, capnp::AnyPointer>) /home/src/bitcoin/build/src/ipc/libmultiprocess/include/mp/proxy.capnp.c++:602:9 (bitcoin-node+0x17ad47e)
        #4 mp::ThreadMap::Server::dispatchCall(unsigned long, unsigned short, capnp::CallContext<capnp::AnyPointer, capnp::AnyPointer>) /home/src/bitcoin/build/src/ipc/libmultiprocess/include/mp/proxy.capnp.c++:591:14 (bitcoin-node+0x17ad2f1)
        #5 virtual thunk to mp::ThreadMap::Server::dispatchCall(unsigned long, unsigned short, capnp::CallContext<capnp::AnyPointer, capnp::AnyPointer>) /home/src/bitcoin/build/src/ipc/libmultiprocess/include/mp/proxy.capnp.c++ (bitcoin-node+0x17ad5f7)
        #6 capnp::LocalClient::callInternal(unsigned long, unsigned short, capnp::CallContextHook&) <null> (libcapnp-rpc.so.1.2.0+0x747d6) (BuildId: 98f819a103310afbd8f791a0c80d203a4d1b9791)
        #7 mp::EventLoop::loop() /home/src/bitcoin/build/src/ipc/libmultiprocess/./ipc/libmultiprocess/src/mp/proxy.cpp:244:68 (bitcoin-node+0x17b135e)
        #8 ipc::capnp::(anonymous namespace)::CapnpProtocol::startLoop(char const*)::'lambda'()::operator()() const /home/src/bitcoin/build/src/ipc/./ipc/capnp/protocol.cpp:136:21 (bitcoin-node+0xe516e4)
        #9 void std::__invoke_impl<void, ipc::capnp::(anonymous namespace)::CapnpProtocol::startLoop(char const*)::'lambda'()>(std::__invoke_other, ipc::capnp::(anonymous namespace)::CapnpProtocol::startLoop(char const*)::'lambda'()&&) /nix/store/kzq78n13l8w24jn8bx4djj79k5j717f1-gcc-14.3.0/include/c++/14.3.0/bits/invoke.h:61:14 (bitcoin-node+0xe514a2)
        #10 std::__invoke_result<ipc::capnp::(anonymous namespace)::CapnpProtocol::startLoop(char const*)::'lambda'()>::type std::__invoke<ipc::capnp::(anonymous namespace)::CapnpProtocol::startLoop(char const*)::'lambda'()>(ipc::capnp::(anonymous namespace)::CapnpProtocol::startLoop(char const*)::'lambda'()&&) /nix/store/kzq78n13l8w24jn8bx4djj79k5j717f1-gcc-14.3.0/include/c++/14.3.0/bits/invoke.h:96:14 (bitcoin-node+0xe513a2)
        #11 void std::thread::_Invoker<std::tuple<ipc::capnp::(anonymous namespace)::CapnpProtocol::startLoop(char const*)::'lambda'()>>::_M_invoke<0ul>(std::_Index_tuple<0ul>) /nix/store/kzq78n13l8w24jn8bx4djj79k5j717f1-gcc-14.3.0/include/c++/14.3.0/bits/std_thread.h:301:13 (bitcoin-node+0xe5130a)
        #12 std::thread::_Invoker<std::tuple<ipc::capnp::(anonymous namespace)::CapnpProtocol::startLoop(char const*)::'lambda'()>>::operator()() /nix/store/kzq78n13l8w24jn8bx4djj79k5j717f1-gcc-14.3.0/include/c++/14.3.0/bits/std_thread.h:308:11 (bitcoin-node+0xe51272)
        #13 std::thread::_State_impl<std::thread::_Invoker<std::tuple<ipc::capnp::(anonymous namespace)::CapnpProtocol::startLoop(char const*)::'lambda'()>>>::_M_run() /nix/store/kzq78n13l8w24jn8bx4djj79k5j717f1-gcc-14.3.0/include/c++/14.3.0/bits/std_thread.h:253:13 (bitcoin-node+0xe5106a)
        #14 execute_native_thread_routine <null> (libstdc++.so.6+0xed063)
    
    SUMMARY: ThreadSanitizer: data race (/home/src/bitcoin/build/bin/bitcoin-node+0x1c1196) in operator delete(void*)
    ==================
    ==================
    WARNING: ThreadSanitizer: data race (pid=86917)
      Write of size 8 at 0x723c00034cb0 by thread T2:
        #0 operator delete(void*, unsigned long) <null> (bitcoin-node+0x1c1616)
        #1 kj::_::AttachmentPromiseNode<kj::_::Tuple<kj::Own<capnp::LocalClient, std::nullptr_t>, kj::Own<capnp::CallContextHook, std::nullptr_t>>>::~AttachmentPromiseNode() <null> (libcapnp-rpc.so.1.2.0+0x75c5f) (BuildId: 98f819a103310afbd8f791a0c80d203a4d1b9791)
        #2 mp::EventLoop::loop() /home/src/bitcoin/build/src/ipc/libmultiprocess/./ipc/libmultiprocess/src/mp/proxy.cpp:244:68 (bitcoin-node+0x17b135e)
        #3 ipc::capnp::(anonymous namespace)::CapnpProtocol::startLoop(char const*)::'lambda'()::operator()() const /home/src/bitcoin/build/src/ipc/./ipc/capnp/protocol.cpp:136:21 (bitcoin-node+0xe516e4)
        #4 void std::__invoke_impl<void, ipc::capnp::(anonymous namespace)::CapnpProtocol::startLoop(char const*)::'lambda'()>(std::__invoke_other, ipc::capnp::(anonymous namespace)::CapnpProtocol::startLoop(char const*)::'lambda'()&&) /nix/store/kzq78n13l8w24jn8bx4djj79k5j717f1-gcc-14.3.0/include/c++/14.3.0/bits/invoke.h:61:14 (bitcoin-node+0xe514a2)
        #5 std::__invoke_result<ipc::capnp::(anonymous namespace)::CapnpProtocol::startLoop(char const*)::'lambda'()>::type std::__invoke<ipc::capnp::(anonymous namespace)::CapnpProtocol::startLoop(char const*)::'lambda'()>(ipc::capnp::(anonymous namespace)::CapnpProtocol::startLoop(char const*)::'lambda'()&&) /nix/store/kzq78n13l8w24jn8bx4djj79k5j717f1-gcc-14.3.0/include/c++/14.3.0/bits/invoke.h:96:14 (bitcoin-node+0xe513a2)
        #6 void std::thread::_Invoker<std::tuple<ipc::capnp::(anonymous namespace)::CapnpProtocol::startLoop(char const*)::'lambda'()>>::_M_invoke<0ul>(std::_Index_tuple<0ul>) /nix/store/kzq78n13l8w24jn8bx4djj79k5j717f1-gcc-14.3.0/include/c++/14.3.0/bits/std_thread.h:301:13 (bitcoin-node+0xe5130a)
        #7 std::thread::_Invoker<std::tuple<ipc::capnp::(anonymous namespace)::CapnpProtocol::startLoop(char const*)::'lambda'()>>::operator()() /nix/store/kzq78n13l8w24jn8bx4djj79k5j717f1-gcc-14.3.0/include/c++/14.3.0/bits/std_thread.h:308:11 (bitcoin-node+0xe51272)
        #8 std::thread::_State_impl<std::thread::_Invoker<std::tuple<ipc::capnp::(anonymous namespace)::CapnpProtocol::startLoop(char const*)::'lambda'()>>>::_M_run() /nix/store/kzq78n13l8w24jn8bx4djj79k5j717f1-gcc-14.3.0/include/c++/14.3.0/bits/std_thread.h:253:13 (bitcoin-node+0xe5106a)
        #9 execute_native_thread_routine <null> (libstdc++.so.6+0xed063)
    
      Previous read of size 8 at 0x723c00034cb0 by thread T13:
        #0 capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>::getParams() /nix/store/8xjrdb1y9ng6w6yknzg9h6fkxd3sb824-capnproto-1.2.0/include/capnp/capability.h:1129:16 (bitcoin-node+0x1213722)
        #1 void mp::PassField<mp::Accessor<mp::mining_fields::Options, 17>, node::BlockWaitOptions, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>>(mp::Priority<0>, mp::TypeList<node::BlockWaitOptions>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall> const&, mp::TypeList<>&&) /home/src/bitcoin/build/src/ipc/./ipc/libmultiprocess/include/mp/proxy-types.h:296:54 (bitcoin-node+0x12209f5)
        #2 decltype(auto) mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>::invoke<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>, mp::TypeList<node::BlockWaitOptions>>(mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&, mp::TypeList<node::BlockWaitOptions>) const /home/src/bitcoin/build/src/ipc/./ipc/libmultiprocess/include/mp/proxy-types.h:549:16 (bitcoin-node+0x12208ea)
        #3 std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<node::BlockWaitOptions>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<node::BlockWaitOptions>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda1'()::operator()() const /home/src/bitcoin/build/src/ipc/./ipc/libmultiprocess/include/mp/type-context.h:138:21 (bitcoin-node+0x1220534)
        #4 kj::Maybe<kj::Exception> kj::runCatchingExceptions<std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<node::BlockWaitOptions>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<node::BlockWaitOptions>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&)::'lambda1'()>(mp::Accessor<mp::mining_fields::Context, 17>&&) /nix/store/8xjrdb1y9ng6w6yknzg9h6fkxd3sb824-capnproto-1.2.0/include/kj/exception.h:371:5 (bitcoin-node+0x121d6a1)
        #5 std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<node::BlockWaitOptions>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<node::BlockWaitOptions>&&)::'lambda'(mp::CancelMonitor&)::operator()(mp::CancelMonitor&) /home/src/bitcoin/build/src/ipc/./ipc/libmultiprocess/include/mp/type-context.h:138:21 (bitcoin-node+0x121cc6c)
        #6 kj::Promise<mp::Accessor<mp::mining_fields::Context, 17>> mp::ProxyServer<mp::Thread>::post<capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>, std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<node::BlockWaitOptions>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<node::BlockWaitOptions>&&)::'lambda'(mp::CancelMonitor&)>(mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&&)::'lambda'()::operator()()::'lambda'()::operator()()::'lambda0'()::operator()() const /home/src/bitcoin/build/src/ipc/./ipc/libmultiprocess/include/mp/proxy-io.h:720:100 (bitcoin-node+0x121c4d3)
        #7 kj::Maybe<kj::Exception> kj::runCatchingExceptions<kj::Promise<mp::Accessor<mp::mining_fields::Context, 17>> mp::ProxyServer<mp::Thread>::post<capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>, std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<node::BlockWaitOptions>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<node::BlockWaitOptions>&&)::'lambda'(mp::CancelMonitor&)>(mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&&)::'lambda'()::operator()()::'lambda'()::operator()()::'lambda0'()>(mp::Accessor<mp::mining_fields::Context, 17>&&) /nix/store/8xjrdb1y9ng6w6yknzg9h6fkxd3sb824-capnproto-1.2.0/include/kj/exception.h:371:5 (bitcoin-node+0x121b371)
        #8 kj::Promise<mp::Accessor<mp::mining_fields::Context, 17>> mp::ProxyServer<mp::Thread>::post<capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>, std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<node::BlockWaitOptions>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<node::BlockWaitOptions>&&)::'lambda'(mp::CancelMonitor&)>(mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&&)::'lambda'()::operator()()::'lambda'()::operator()() /home/src/bitcoin/build/src/ipc/./ipc/libmultiprocess/include/mp/proxy-io.h:720:48 (bitcoin-node+0x121aeb2)
        #9 kj::Function<void ()>::Impl<kj::Promise<mp::Accessor<mp::mining_fields::Context, 17>> mp::ProxyServer<mp::Thread>::post<capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>, std::enable_if<std::is_same<decltype(mp::Accessor<mp::mining_fields::Context, 17>::get(fp1.call_context.getParams())), mp::Context::Reader>::value, kj::Promise<mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>::CallContext>>::type mp::PassField<mp::Accessor<mp::mining_fields::Context, 17>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>>, mp::TypeList<node::BlockWaitOptions>>(mp::Priority<1>, mp::TypeList<>, mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&, mp::ServerField<1, mp::Accessor<mp::mining_fields::Options, 17>, mp::ServerRet<mp::Accessor<mp::mining_fields::Result, 18>, mp::ServerCall>> const&, mp::TypeList<node::BlockWaitOptions>&&)::'lambda'(mp::CancelMonitor&)>(mp::ServerInvokeContext<mp::ProxyServer<ipc::capnp::messages::BlockTemplate>, capnp::CallContext<ipc::capnp::messages::BlockTemplate::WaitNextParams, ipc::capnp::messages::BlockTemplate::WaitNextResults>>&&)::'lambda'()::operator()()::'lambda'()>::operator()() /nix/store/8xjrdb1y9ng6w6yknzg9h6fkxd3sb824-capnproto-1.2.0/include/kj/function.h:142:14 (bitcoin-node+0x121acfa)
        #10 kj::Function<void ()>::operator()() /nix/store/8xjrdb1y9ng6w6yknzg9h6fkxd3sb824-capnproto-1.2.0/include/kj/function.h:119:12 (bitcoin-node+0xe8e221)
        #11 void mp::Unlock<mp::Lock, kj::Function<void ()>&>(mp::Lock&, kj::Function<void ()>&) /home/src/bitcoin/build/src/ipc/./ipc/libmultiprocess/include/mp/util.h:210:5 (bitcoin-node+0xe8dd31)
        #12 void mp::Waiter::wait<mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0::operator()() const::'lambda'()>(mp::Lock&, mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0::operator()() const::'lambda'())::'lambda'()::operator()() const /home/src/bitcoin/build/src/ipc/libmultiprocess/./ipc/libmultiprocess/include/mp/proxy-io.h:371:17 (bitcoin-node+0x17b827f)
        #13 void std::condition_variable::wait<void mp::Waiter::wait<mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0::operator()() const::'lambda'()>(mp::Lock&, mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0::operator()() const::'lambda'())::'lambda'()>(std::unique_lock<std::mutex>&, mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0::operator()() const::'lambda'()) /nix/store/kzq78n13l8w24jn8bx4djj79k5j717f1-gcc-14.3.0/include/c++/14.3.0/condition_variable:104:10 (bitcoin-node+0x17b815e)
        #14 void mp::Waiter::wait<mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0::operator()() const::'lambda'()>(mp::Lock&, mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0::operator()() const::'lambda'()) /home/src/bitcoin/build/src/ipc/libmultiprocess/./ipc/libmultiprocess/include/mp/proxy-io.h:362:14 (bitcoin-node+0x17b7fdf)
        #15 mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0::operator()() const /home/src/bitcoin/build/src/ipc/libmultiprocess/./ipc/libmultiprocess/src/mp/proxy.cpp:423:34 (bitcoin-node+0x17b7e39)
        #16 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/kzq78n13l8w24jn8bx4djj79k5j717f1-gcc-14.3.0/include/c++/14.3.0/bits/invoke.h:61:14 (bitcoin-node+0x17b7ba2)
        #17 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/kzq78n13l8w24jn8bx4djj79k5j717f1-gcc-14.3.0/include/c++/14.3.0/bits/invoke.h:96:14 (bitcoin-node+0x17b7aa2)
        #18 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/kzq78n13l8w24jn8bx4djj79k5j717f1-gcc-14.3.0/include/c++/14.3.0/bits/std_thread.h:301:13 (bitcoin-node+0x17b7a0a)
        #19 std::thread::_Invoker<std::tuple<mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>)::$_0>>::operator()() /nix/store/kzq78n13l8w24jn8bx4djj79k5j717f1-gcc-14.3.0/include/c++/14.3.0/bits/std_thread.h:308:11 (bitcoin-node+0x17b7972)
        #20 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/kzq78n13l8w24jn8bx4djj79k5j717f1-gcc-14.3.0/include/c++/14.3.0/bits/std_thread.h:253:13 (bitcoin-node+0x17b745a)
        #21 execute_native_thread_routine <null> (libstdc++.so.6+0xed063)
    
      Thread T2 'b-capnp-loop' (tid=86923, running) created by main thread at:
        #0 pthread_create <null> (bitcoin-node+0x13a395)
        #1 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State>>, void (*)()) <null> (libstdc++.so.6+0xed138)
        #2 ipc::capnp::(anonymous namespace)::CapnpProtocol::startLoop(char const*) /home/src/bitcoin/build/src/ipc/./ipc/capnp/protocol.cpp:127:25 (bitcoin-node+0xe50b88)
        #3 ipc::capnp::(anonymous namespace)::CapnpProtocol::listen(int, char const*, interfaces::Init&) /home/src/bitcoin/build/src/ipc/./ipc/capnp/protocol.cpp:87:9 (bitcoin-node+0xe503b2)
        #4 ipc::(anonymous namespace)::IpcImpl::listenAddress(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>&) /home/src/bitcoin/build/src/ipc/./ipc/interfaces.cpp:111:21 (bitcoin-node+0xe475c5)
        #5 AppInitMain(node::NodeContext&, interfaces::BlockAndHeaderTipInfo*) /home/src/bitcoin/build/src/./init.cpp:1483:22 (bitcoin-node+0x1f8a67)
        #6 AppInit(node::NodeContext&) /home/src/bitcoin/build/src/./bitcoind.cpp:242:43 (bitcoin-node+0x1c42fd)
        #7 main /home/src/bitcoin/build/src/./bitcoind.cpp:283:10 (bitcoin-node+0x1c2a95)
    
      Thread T13 (tid=86942, running) created by thread T2 at:
        #0 pthread_create <null> (bitcoin-node+0x13a395)
        #1 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State>>, void (*)()) <null> (libstdc++.so.6+0xed138)
        #2 mp::ProxyServer<mp::ThreadMap>::makeThread(capnp::CallContext<mp::ThreadMap::MakeThreadParams, mp::ThreadMap::MakeThreadResults>) /home/src/bitcoin/build/src/ipc/libmultiprocess/./ipc/libmultiprocess/src/mp/proxy.cpp:416:17 (bitcoin-node+0x17b3821)
        #3 mp::ThreadMap::Server::dispatchCallInternal(unsigned short, capnp::CallContext<capnp::AnyPointer, capnp::AnyPointer>) /home/src/bitcoin/build/src/ipc/libmultiprocess/include/mp/proxy.capnp.c++:602:9 (bitcoin-node+0x17ad47e)
        #4 mp::ThreadMap::Server::dispatchCall(unsigned long, unsigned short, capnp::CallContext<capnp::AnyPointer, capnp::AnyPointer>) /home/src/bitcoin/build/src/ipc/libmultiprocess/include/mp/proxy.capnp.c++:591:14 (bitcoin-node+0x17ad2f1)
        #5 virtual thunk to mp::ThreadMap::Server::dispatchCall(unsigned long, unsigned short, capnp::CallContext<capnp::AnyPointer, capnp::AnyPointer>) /home/src/bitcoin/build/src/ipc/libmultiprocess/include/mp/proxy.capnp.c++ (bitcoin-node+0x17ad5f7)
        #6 capnp::LocalClient::callInternal(unsigned long, unsigned short, capnp::CallContextHook&) <null> (libcapnp-rpc.so.1.2.0+0x747d6) (BuildId: 98f819a103310afbd8f791a0c80d203a4d1b9791)
        #7 mp::EventLoop::loop() /home/src/bitcoin/build/src/ipc/libmultiprocess/./ipc/libmultiprocess/src/mp/proxy.cpp:244:68 (bitcoin-node+0x17b135e)
        #8 ipc::capnp::(anonymous namespace)::CapnpProtocol::startLoop(char const*)::'lambda'()::operator()() const /home/src/bitcoin/build/src/ipc/./ipc/capnp/protocol.cpp:136:21 (bitcoin-node+0xe516e4)
        #9 void std::__invoke_impl<void, ipc::capnp::(anonymous namespace)::CapnpProtocol::startLoop(char const*)::'lambda'()>(std::__invoke_other, ipc::capnp::(anonymous namespace)::CapnpProtocol::startLoop(char const*)::'lambda'()&&) /nix/store/kzq78n13l8w24jn8bx4djj79k5j717f1-gcc-14.3.0/include/c++/14.3.0/bits/invoke.h:61:14 (bitcoin-node+0xe514a2)
        #10 std::__invoke_result<ipc::capnp::(anonymous namespace)::CapnpProtocol::startLoop(char const*)::'lambda'()>::type std::__invoke<ipc::capnp::(anonymous namespace)::CapnpProtocol::startLoop(char const*)::'lambda'()>(ipc::capnp::(anonymous namespace)::CapnpProtocol::startLoop(char const*)::'lambda'()&&) /nix/store/kzq78n13l8w24jn8bx4djj79k5j717f1-gcc-14.3.0/include/c++/14.3.0/bits/invoke.h:96:14 (bitcoin-node+0xe513a2)
        #11 void std::thread::_Invoker<std::tuple<ipc::capnp::(anonymous namespace)::CapnpProtocol::startLoop(char const*)::'lambda'()>>::_M_invoke<0ul>(std::_Index_tuple<0ul>) /nix/store/kzq78n13l8w24jn8bx4djj79k5j717f1-gcc-14.3.0/include/c++/14.3.0/bits/std_thread.h:301:13 (bitcoin-node+0xe5130a)
        #12 std::thread::_Invoker<std::tuple<ipc::capnp::(anonymous namespace)::CapnpProtocol::startLoop(char const*)::'lambda'()>>::operator()() /nix/store/kzq78n13l8w24jn8bx4djj79k5j717f1-gcc-14.3.0/include/c++/14.3.0/bits/std_thread.h:308:11 (bitcoin-node+0xe51272)
        #13 std::thread::_State_impl<std::thread::_Invoker<std::tuple<ipc::capnp::(anonymous namespace)::CapnpProtocol::startLoop(char const*)::'lambda'()>>>::_M_run() /nix/store/kzq78n13l8w24jn8bx4djj79k5j717f1-gcc-14.3.0/include/c++/14.3.0/bits/std_thread.h:253:13 (bitcoin-node+0xe5106a)
        #14 execute_native_thread_routine <null> (libstdc++.so.6+0xed063)
    
    SUMMARY: ThreadSanitizer: data race (/home/src/bitcoin/build/bin/bitcoin-node+0x1c1616) in operator delete(void*, unsigned long)
    ==================
    ThreadSanitizer: reported 2 warnings
    4f42fc4ddc
  14. in include/mp/proxy-io.h:91 in 1bcf4ac1b5 outdated
    87     ~ProxyServer();
    88     kj::Promise<void> getName(GetNameContext context) override;
    89+
    90+    //! Run a callback function returning T on this thread.
    91+    template<typename T, typename Fn>
    92+    kj::Promise<T> post(Fn&& fn);
    


    Sjors commented at 10:47 am on February 11, 2026:
    In 1bcf4ac1b58b960e57d2c794d9e0df5e1722b3f6 Allow simultaneous calls on same Context.thread: if this function makes sense on it’s own, is it possible to introduce it in a preparation commit? Both commits in this PR make use of it.

    ryanofsky commented at 8:02 pm on February 11, 2026:

    re: #240 (review)

    In 1bcf4ac Allow simultaneous calls on same Context.thread: if this function makes sense on it’s own, is it possible to introduce it in a preparation commit? Both commits in this PR make use of it.

    Yes, that’s a really nice idea. Added a new refactoring commit to move code into this function before changing it.

  15. ryanofsky commented at 8:05 pm on February 11, 2026: collaborator

    re: #240 (comment)

    should be fixable by having the worker thread acquire a lock in the brief period …

    @ryanofsky is that something you want to add here?

    Yes thanks, added a new commit which does this and fixes the TSAN error locally for me. Will update https://github.com/bitcoin/bitcoin/pull/34422 and confirm it fixes the failure there too.


    Rebased 1f307fb8fd1aff4a2e97b8f179b4162049f7af50 -> 1d7debfa8ea9a5eddfcf3f8bb694e4971e04d61d (pr/promise.3 -> pr/promise.4, compare) adding result_value.reset() call to try fix CI error https://github.com/bitcoin/bitcoin/actions/runs/21412348994/job/61652239914?pr=34422

    Updated 1d7debfa8ea9a5eddfcf3f8bb694e4971e04d61d -> 4f42fc4ddcb9202b2ac3de9a2971325c088e5a5c (pr/promise.4 -> pr/promise.5, compare) to fix CI error https://github.com/bitcoin/bitcoin/actions/runs/21421194207/job/61680781122?pr=34422

  16. ryanofsky force-pushed on Feb 11, 2026
  17. theuni commented at 11:35 pm on February 12, 2026: contributor
    @ryanofsky I’m having a bit of trouble understanding what’s happening on which thread, but it seems like a mutex is not the most straightforward solution here? It’s not immediately obvious to me what it’s guarding, anyway. Perhaps a simple std::atomic_flag would be better to communicate “I’ve reached this point”? You could set/notify when the params are safe to delete, then in m_on_cancel you could just wait for it?
  18. ryanofsky commented at 3:24 am on February 13, 2026: collaborator

    @ryanofsky I’m having a bit of trouble understanding what’s happening on which thread, but it seems like a mutex is not the most straightforward solution here? It’s not immediately obvious to me what it’s guarding, anyway. Perhaps a simple std::atomic_flag would be better to communicate “I’ve reached this point”? You could set/notify when the params are safe to delete, then in m_on_cancel you could just wait for it?

    Thanks for looking at this! I assume you are asking about the last commit 4f42fc4ddcb9202b2ac3de9a2971325c088e5a5c which is fixing the race condition detected by TSAN.

    The issue being fixed in that commit is a race between two threads both potentially accessing capnproto request parameters in an incoming request.

    • One thread is a worker thread running the big invoke lambda which is responsible for converting the capnproto request parameters into c++ arguments, and then calling a c++ method with those arguments, and then converting the c++ method’s return value & output arguments or exception into a capnproto response.
    • The other thread is the capnproto event loop thread running the on_cancel lambda when there is a disconnect or cancellation. After the on_cancel lambda returns, capnproto deletes the request parameters.

    The point of the mutex is to prevent the event loop thread from deleting the request parameters while the worker thread is still reading them.

    The control flow is a little complicated because of the way parameters are unpacked recursively, but the idea of guarding access to the parameters with a mutex is pretty simple. The params_mutex is locked by the worker thread before it accesses the capnproto parameters, and unlocked by that thread after all the parameters have been unpacked and it is ready to call the c++ method that is supposed to be executed. Then on the event loop thread inside the on_cancel lambda, the same mutex is locked before it returns so if there is a cancellation while the worker thread is unpacking parameters, the event loop thread will be blocked, and not delete the parameters until after the worker thread has finished reading them.

    What is odd about this fix is that the event loop thread is releasing the mutex before it actually deletes the parameters. This is suspicious, because if the mutex is not locked while they are deleted it does nothing to solve the reverse race condition in the case where the cancellation happens before the parameters start being read. But this case is handled by a separate cancel_monitor.m_cancelled check which raises an exception to prevent the worker thread from reading the parameters.

    So the mutex should solve delete-while-reading race condition in a fairly straightforward way even if the control flow is complicated by a lot of recursion and callbacks, and even if it doesn’t solve the related delete-before-reading race conditions.

    Tangent: One observation looking at the code is that while the delete-before-reading reverse case is being handled, it isn’t being handled very well because the exception raised is thrown inside the sync block instead of outside of it, and it should probably be an InterruptException instead of an MP_LOG call so it can be caught higher in the stack and the thread can be cleaned up. I’m guessing right now that code will cause the worker thread to hang be leaked, never getting cleaned up. Ideally I’ could write a unit test trying to trigger this, but so far I’ve been struggling to figure out how to test different possible races and make sure they are handled well. It’s not obvious how to arrange the the request being cancelled before parameters are read. Anyway, this is only indirectly related to the TSAN race addressed in the commit, which is also unlikely to happen in practice and probably hasn’t happened, even though TSAN was right to warn the request parameters were previously being accessed from different threads without synchronization between them.

  19. theuni commented at 4:50 pm on February 13, 2026: contributor

    @ryanofsky Thanks for the explanation, that was very helpful.

    I’m still struggling to understand how this mutex is working though. It seems that since it’s already been unlocked in invoke(), the mutex will already be unlocked on the worker thread when params_lock goes out of scope, which would cause a double unlock (undefined), no?

  20. Sjors commented at 4:55 pm on February 13, 2026: member

    I created a branch and had an agent splinter 558ae11d8f2031cb5ae6f441c85621a6d662090e refactor: Add ProxyServer::post() method into multiple tiny commits: sjors/2026/02/558ae11-split

    No need to use it here, but it might aid others in reviewing (with dimmed-zebra). Noticed a small behavior change in that it removes MP_LOG for thread busy. It’s harmless since 92fed35b0b57e229fb5c009561f3f077e832bca0 drops this exception anyway.

    Also drew a little diagram to orient myself about where this function lives (IIUC):

    Review placeholder: the first two commits, up to 92fed35b0b57e229fb5c009561f3f077e832bca0, mostly make sense to me now.

  21. theuni commented at 5:08 pm on February 13, 2026: contributor

    I’m still struggling to understand how this mutex is working though. It seems that since it’s already been unlocked in invoke(), the mutex will already be unlocked on the worker thread when params_lock goes out of scope, which would cause a double unlock (undefined), no?

    Nevermind, I see my mistake now. I was thinking it was the bare mutex that was being unlocked in invoke(), but it’s the lock itself. Seems sane I suppose, just not exactly straightforward.


github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin-core/libmultiprocess. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-02-17 00:30 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me