bug: `EventLoop::post()` deadlocks when posted function throws #259

issue hulxv opened this issue on March 19, 2026
  1. hulxv commented at 7:46 PM on March 19, 2026: contributor

    Description

    EventLoop::loop() executes posted functions inside a for(;;) loop without exception safety on the cleanup path. If the posted function throws, m_post_fn is never reset to nullptr and m_cv.notify_all() is never called. The calling thread, blocked in post() waiting for m_post_fn != &fn, hangs forever.

    The cleanup code after the loop (closing file descriptors, resetting m_async_fns, notifying waiters) also runs outside any RAII guard, so all of it is skipped during stack unwinding.

    In loop():

    if (m_post_fn) {
        Unlock(lock, *m_post_fn);   // exception propagates from here
        m_post_fn = nullptr;         // skipped
        m_cv.notify_all();           // skipped
    }
    

    In post():

    // waits forever: m_post_fn still == &fn, nobody notifies
    m_cv.wait(lock.m_lock, [this, &fn]() { return m_post_fn != &fn; });
    

    After the loop exits via exception, ~EventLoop() hits KJ_ASSERT(m_post_fn == nullptr) and aborts. If assertions are disabled, the condition variable is destroyed while the posting thread still waits on it (undefined behavior).

    Current callers avoid this because clientInvoke captures exceptions in std::exception_ptr rather than letting them propagate. The bug is latent but violates exception safety guarantees and will break any future caller that throws from a posted function.

    Reproducer

    #include <mp/proxy-io.h>
    #include <mp/util.h>
    
    #include <cstdio>
    #include <future>
    #include <stdexcept>
    #include <thread>
    
    int main()
    {
        std::promise<mp::EventLoop*> loop_ready;
    
        std::thread loop_thread([&] {
            mp::EventLoop loop("bugtest", [](mp::LogMessage log) {
                if (log.level == mp::Log::Raise)
                    throw std::runtime_error(log.message);
            });
    
            loop_ready.set_value(&loop);
    
            try {
                loop.loop();
            } catch (const std::exception& e) {
                fprintf(stderr, "loop() threw: %s\n", e.what());
            }
            // ~EventLoop fires KJ_ASSERT(m_post_fn == nullptr) here
        });
    
        mp::EventLoop* loop = loop_ready.get_future().get();
    
        std::thread post_thread([&] {
            loop->post(kj::Function<void()>([] {
                throw std::runtime_error("throw from posted fn");
            }));
        });
    
        post_thread.detach();
        loop_thread.join();
        return 0;
    }
    

    Run under Helgrind:

    $ valgrind --tool=helgrind ./post_race_test
    
    ==276139== Helgrind, a thread error detector
    ==276139== Copyright (C) 2007-2024, and GNU GPL'd, by OpenWorks LLP et al.
    ==276139== Using Valgrind-3.25.1 and LibVEX; rerun with -h for copyright info
    ==276139== Command: ./test/post_race_test
    ==276139== 
    ==276139== ---Thread-Announcement------------------------------------------
    ==276139== 
    ==276139== Thread [#1](/bitcoin-core-multiprocess/1/) is the program's root thread
    ==276139== 
    ==276139== ---Thread-Announcement------------------------------------------
    ==276139== 
    ==276139== Thread [#2](/bitcoin-core-multiprocess/2/) was created
    ==276139==    at 0x50BF823: clone (in /usr/lib/libc.so.6)
    ==276139==    by 0x50BF980: ??? (in /usr/lib/libc.so.6)
    ==276139==    by 0x503B4ED: ??? (in /usr/lib/libc.so.6)
    ==276139==    by 0x503C16B: pthread_create (in /usr/lib/libc.so.6)
    ==276139==    by 0x4CB96A1: std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State> >, void (*)()) (in /usr/lib/libstdc++.so.6.0.34)
    ==276139==    by 0x4008F22: std::thread::thread<main::{lambda()#1}, , void>(main::{lambda()#1}&&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4008D0B: main (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139== 
    ==276139== ----------------------------------------------------------------
    ==276139== 
    ==276139== Possible data race during read of size 8 at 0x51CE098 by thread [#1](/bitcoin-core-multiprocess/1/)
    ==276139== Locks held: none
    ==276139==    at 0x400CE00: std::__uniq_ptr_impl<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter>::_M_ptr() const (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x400C1C1: std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter>::get() const (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x400B4DD: std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter>::operator*() const (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x400A9BC: std::__future_base::_State_baseV2::wait() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x400C861: std::__basic_future<mp::EventLoop*>::_M_get_result() const (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x400BD32: std::future<mp::EventLoop*>::get() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4008D2A: main (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139== 
    ==276139== This conflicts with a previous write of size 8 by thread [#2](/bitcoin-core-multiprocess/2/)
    ==276139== Locks held: none
    ==276139==    at 0x400CED2: std::enable_if<std::__and_<std::__not_<std::__is_tuple_like<std::__future_base::_Result_base*> >, std::is_move_constructible<std::__future_base::_Result_base*>, std::is_move_assignable<std::__future_base::_Result_base*> >::value, void>::type std::swap<std::__future_base::_Result_base*>(std::__future_base::_Result_base*&, std::__future_base::_Result_base*&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x400C318: std::__uniq_ptr_impl<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter>::swap(std::__uniq_ptr_impl<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter>&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x400B7D6: std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter>::swap(std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter>&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x400ACCF: std::__future_base::_State_baseV2::_M_do_set(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x400CE89: void std::__invoke_impl<void, void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(std::__invoke_memfun_deref, void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x400C213: std::__invoke_result<void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>::type std::__invoke<void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x400B574: std::call_once<void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(std::once_flag&, void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&)::{lambda()#1}::operator()() const (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x400C236: std::once_flag::_Prepare_execution::_Prepare_execution<std::call_once<void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(std::once_flag&, void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&)::{lambda()#1}>(void (std::__future_base::_State_baseV2::*&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*))::{lambda()#1}::operator()() const (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==  Address 0x51ce098 is 24 bytes inside a block of size 48 alloc'd
    ==276139==    at 0x488C093: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
    ==276139==    by 0x400ECB8: std::__new_allocator<std::_Sp_counted_ptr_inplace<std::__future_base::_State_baseV2, std::allocator<void>, (__gnu_cxx::_Lock_policy)2> >::allocate(unsigned long, void const*) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x400E3A6: std::__allocated_ptr<std::allocator<std::_Sp_counted_ptr_inplace<std::__future_base::_State_baseV2, std::allocator<void>, (__gnu_cxx::_Lock_policy)2> > > std::__allocate_guarded<std::allocator<std::_Sp_counted_ptr_inplace<std::__future_base::_State_baseV2, std::allocator<void>, (__gnu_cxx::_Lock_policy)2> > >(std::allocator<std::_Sp_counted_ptr_inplace<std::__future_base::_State_baseV2, std::allocator<void>, (__gnu_cxx::_Lock_policy)2> >&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x400D98C: std::__shared_count<(__gnu_cxx::_Lock_policy)2>::__shared_count<std::__future_base::_State_baseV2, std::allocator<void>>(std::__future_base::_State_baseV2*&, std::_Sp_alloc_shared_tag<std::allocator<void> >) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x400D009: std::__shared_ptr<std::__future_base::_State_baseV2, (__gnu_cxx::_Lock_policy)2>::__shared_ptr<std::allocator<void>>(std::_Sp_alloc_shared_tag<std::allocator<void> >) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x400C402: std::shared_ptr<std::__future_base::_State_baseV2>::shared_ptr<std::allocator<void>>(std::_Sp_alloc_shared_tag<std::allocator<void> >) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x400B8E6: std::shared_ptr<std::__future_base::_State_baseV2> std::make_shared<std::__future_base::_State_baseV2>() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x400BA6D: std::promise<mp::EventLoop*>::promise() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4008CF0: main (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==  Block was alloc'd by thread [#1](/bitcoin-core-multiprocess/1/)
    ==276139== 
    ==276139== ----------------------------------------------------------------
    ==276139== 
    ==276139== Possible data race during read of size 8 at 0x51CE100 by thread [#1](/bitcoin-core-multiprocess/1/)
    ==276139== Locks held: none
    ==276139==    at 0x400BD43: std::future<mp::EventLoop*>::get() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4008D2A: main (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139== 
    ==276139== This conflicts with a previous write of size 8 by thread [#2](/bitcoin-core-multiprocess/2/)
    ==276139== Locks held: none
    ==276139==    at 0x400EB56: std::__future_base::_Result<mp::EventLoop*>::_M_set(mp::EventLoop*&&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x400E8FA: std::__future_base::_State_baseV2::_Setter<mp::EventLoop*, mp::EventLoop*&&>::operator()() const (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x400E617: std::unique_ptr<std::__future_base::_Result<mp::EventLoop*>, std::__future_base::_Result_base::_Deleter> std::__invoke_impl<std::unique_ptr<std::__future_base::_Result<mp::EventLoop*>, std::__future_base::_Result_base::_Deleter>, std::__future_base::_State_baseV2::_Setter<mp::EventLoop*, mp::EventLoop*&&>&>(std::__invoke_other, std::__future_base::_State_baseV2::_Setter<mp::EventLoop*, mp::EventLoop*&&>&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x400DBDA: std::enable_if<is_invocable_r_v<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter>, std::__future_base::_State_baseV2::_Setter<mp::EventLoop*, mp::EventLoop*&&>&>, std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> >::type std::__invoke_r<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter>, std::__future_base::_State_baseV2::_Setter<mp::EventLoop*, mp::EventLoop*&&>&>(std::__future_base::_State_baseV2::_Setter<mp::EventLoop*, mp::EventLoop*&&>&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x400D1B9: std::_Function_handler<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> (), std::__future_base::_State_baseV2::_Setter<mp::EventLoop*, mp::EventLoop*&&> >::_M_invoke(std::_Any_data const&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x400B839: std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>::operator()() const (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x400ACB1: std::__future_base::_State_baseV2::_M_do_set(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x400CE89: void std::__invoke_impl<void, void (std::__future_base::_State_baseV2::*)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*>(std::__invoke_memfun_deref, void (std::__future_base::_State_baseV2::*&&)(std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*, bool*), std::__future_base::_State_baseV2*&&, std::function<std::unique_ptr<std::__future_base::_Result_base, std::__future_base::_Result_base::_Deleter> ()>*&&, bool*&&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==  Address 0x51ce100 is 16 bytes inside a block of size 32 alloc'd
    ==276139==    at 0x488C093: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
    ==276139==    by 0x400BA7F: std::promise<mp::EventLoop*>::promise() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4008CF0: main (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==  Block was alloc'd by thread [#1](/bitcoin-core-multiprocess/1/)
    ==276139== 
    ==276139== ---Thread-Announcement------------------------------------------
    ==276139== 
    ==276139== Thread [#3](/bitcoin-core-multiprocess/3/) was created
    ==276139==    at 0x50BF823: clone (in /usr/lib/libc.so.6)
    ==276139==    by 0x50BF980: ??? (in /usr/lib/libc.so.6)
    ==276139==    by 0x503B4ED: ??? (in /usr/lib/libc.so.6)
    ==276139==    by 0x503C16B: pthread_create (in /usr/lib/libc.so.6)
    ==276139==    by 0x4CB96A1: std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State> >, void (*)()) (in /usr/lib/libstdc++.so.6.0.34)
    ==276139==    by 0x40090A8: std::thread::thread<main::{lambda()#2}, , void>(main::{lambda()#2}&&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4008D55: main (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139== 
    ==276139== ----------------------------------------------------------------
    ==276139== 
    ==276139== Possible data race during read of size 8 at 0x5DBBB58 by thread [#3](/bitcoin-core-multiprocess/3/)
    ==276139== Locks held: none
    ==276139==    at 0x4011264: mp::EventLoop::post(kj::Function<void ()>) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4008C75: main::{lambda()#2}::operator()() const (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009C30: void std::__invoke_impl<void, main::{lambda()#2}>(std::__invoke_other, main::{lambda()#2}&&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009BAE: std::__invoke_result<main::{lambda()#2}>::type std::__invoke<main::{lambda()#2}>(main::{lambda()#2}&&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009B3D: void std::thread::_Invoker<std::tuple<main::{lambda()#2}> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009AF5: std::thread::_Invoker<std::tuple<main::{lambda()#2}> >::operator()() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009A7F: std::thread::_State_impl<std::thread::_Invoker<std::tuple<main::{lambda()#2}> > >::_M_run() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4CB95A3: ??? (in /usr/lib/libstdc++.so.6.0.34)
    ==276139==    by 0x503B98A: ??? (in /usr/lib/libc.so.6)
    ==276139==    by 0x50BF833: clone (in /usr/lib/libc.so.6)
    ==276139==  Address 0x5dbbb58 is on thread [#2](/bitcoin-core-multiprocess/2/)'s stack
    ==276139==  in frame [#9](/bitcoin-core-multiprocess/9/), created by main::{lambda()#1}::operator()() const (???:)
    ==276139== 
    ==276139== ----------------------------------------------------------------
    ==276139== 
    ==276139== Thread [#2](/bitcoin-core-multiprocess/2/): Bug in libpthread: write lock granted on mutex/rwlock which is currently wr-held by a different thread
    ==276139==    at 0x4894C2A: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
    ==276139==    by 0x4014C11: std::mutex::lock() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4016614: std::unique_lock<std::mutex>::lock() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x401654E: std::unique_lock<std::mutex>::unique_lock(std::mutex&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4014E32: mp::Lock::Lock(mp::Mutex&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4010C2F: mp::EventLoop::loop() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4008ACC: main::{lambda()#1}::operator()() const (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009C6D: void std::__invoke_impl<void, main::{lambda()#1}>(std::__invoke_other, main::{lambda()#1}&&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009BF3: std::__invoke_result<main::{lambda()#1}>::type std::__invoke<main::{lambda()#1}>(main::{lambda()#1}&&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009B69: void std::thread::_Invoker<std::tuple<main::{lambda()#1}> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009B11: std::thread::_Invoker<std::tuple<main::{lambda()#1}> >::operator()() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009ABF: std::thread::_State_impl<std::thread::_Invoker<std::tuple<main::{lambda()#1}> > >::_M_run() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139== 
    ==276139== ----------------------------------------------------------------
    ==276139== 
    ==276139==  Lock at 0x5DBBBA0 was first observed
    ==276139==    at 0x4894C2A: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
    ==276139==    by 0x4014C11: std::mutex::lock() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4016614: std::unique_lock<std::mutex>::lock() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x401654E: std::unique_lock<std::mutex>::unique_lock(std::mutex&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4014E32: mp::Lock::Lock(mp::Mutex&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4010A4B: mp::EventLoop::loop() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4008ACC: main::{lambda()#1}::operator()() const (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009C6D: void std::__invoke_impl<void, main::{lambda()#1}>(std::__invoke_other, main::{lambda()#1}&&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009BF3: std::__invoke_result<main::{lambda()#1}>::type std::__invoke<main::{lambda()#1}>(main::{lambda()#1}&&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009B69: void std::thread::_Invoker<std::tuple<main::{lambda()#1}> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009B11: std::thread::_Invoker<std::tuple<main::{lambda()#1}> >::operator()() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009ABF: std::thread::_State_impl<std::thread::_Invoker<std::tuple<main::{lambda()#1}> > >::_M_run() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==  Address 0x5dbbba0 is on thread [#2](/bitcoin-core-multiprocess/2/)'s stack
    ==276139==  in frame [#1](/bitcoin-core-multiprocess/1/), created by main::{lambda()#1}::operator()() const (???:)
    ==276139== 
    ==276139== Possible data race during read of size 8 at 0x5DBBB68 by thread [#2](/bitcoin-core-multiprocess/2/)
    ==276139== Locks held: none
    ==276139==    at 0x4010C37: mp::EventLoop::loop() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4008ACC: main::{lambda()#1}::operator()() const (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009C6D: void std::__invoke_impl<void, main::{lambda()#1}>(std::__invoke_other, main::{lambda()#1}&&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009BF3: std::__invoke_result<main::{lambda()#1}>::type std::__invoke<main::{lambda()#1}>(main::{lambda()#1}&&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009B69: void std::thread::_Invoker<std::tuple<main::{lambda()#1}> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009B11: std::thread::_Invoker<std::tuple<main::{lambda()#1}> >::operator()() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009ABF: std::thread::_State_impl<std::thread::_Invoker<std::tuple<main::{lambda()#1}> > >::_M_run() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4CB95A3: ??? (in /usr/lib/libstdc++.so.6.0.34)
    ==276139==    by 0x503B98A: ??? (in /usr/lib/libc.so.6)
    ==276139==    by 0x50BF833: clone (in /usr/lib/libc.so.6)
    ==276139== 
    ==276139== This conflicts with a previous write of size 8 by thread [#3](/bitcoin-core-multiprocess/3/)
    ==276139== Locks held: 1, at address 0x5DBBBA0
    ==276139==    at 0x40112D9: mp::EventLoop::post(kj::Function<void ()>) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4008C75: main::{lambda()#2}::operator()() const (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009C30: void std::__invoke_impl<void, main::{lambda()#2}>(std::__invoke_other, main::{lambda()#2}&&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009BAE: std::__invoke_result<main::{lambda()#2}>::type std::__invoke<main::{lambda()#2}>(main::{lambda()#2}&&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009B3D: void std::thread::_Invoker<std::tuple<main::{lambda()#2}> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009AF5: std::thread::_Invoker<std::tuple<main::{lambda()#2}> >::operator()() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009A7F: std::thread::_State_impl<std::thread::_Invoker<std::tuple<main::{lambda()#2}> > >::_M_run() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4CB95A3: ??? (in /usr/lib/libstdc++.so.6.0.34)
    ==276139==  Address 0x5dbbb68 is on thread [#2](/bitcoin-core-multiprocess/2/)'s stack
    ==276139==  in frame [#1](/bitcoin-core-multiprocess/1/), created by main::{lambda()#1}::operator()() const (???:)
    ==276139== 
    ==276139== ----------------------------------------------------------------
    ==276139== 
    ==276139== Thread [#2](/bitcoin-core-multiprocess/2/) unlocked lock at 0x5DBBBA0 currently held by thread [#3](/bitcoin-core-multiprocess/3/)
    ==276139==    at 0x4895369: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
    ==276139==    by 0x4014C5D: std::mutex::unlock() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x40165C1: std::unique_lock<std::mutex>::unlock() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4014E4D: mp::Lock::unlock() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x401A437: mp::UnlockGuard<mp::Lock>::UnlockGuard(mp::Lock&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x401803D: void mp::Unlock<mp::Lock, kj::Function<void ()>&>(mp::Lock&, kj::Function<void ()>&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4010C5C: mp::EventLoop::loop() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4008ACC: main::{lambda()#1}::operator()() const (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009C6D: void std::__invoke_impl<void, main::{lambda()#1}>(std::__invoke_other, main::{lambda()#1}&&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009BF3: std::__invoke_result<main::{lambda()#1}>::type std::__invoke<main::{lambda()#1}>(main::{lambda()#1}&&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009B69: void std::thread::_Invoker<std::tuple<main::{lambda()#1}> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009B11: std::thread::_Invoker<std::tuple<main::{lambda()#1}> >::operator()() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==  Lock at 0x5DBBBA0 was first observed
    ==276139==    at 0x4894C2A: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
    ==276139==    by 0x4014C11: std::mutex::lock() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4016614: std::unique_lock<std::mutex>::lock() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x401654E: std::unique_lock<std::mutex>::unique_lock(std::mutex&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4014E32: mp::Lock::Lock(mp::Mutex&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4010A4B: mp::EventLoop::loop() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4008ACC: main::{lambda()#1}::operator()() const (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009C6D: void std::__invoke_impl<void, main::{lambda()#1}>(std::__invoke_other, main::{lambda()#1}&&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009BF3: std::__invoke_result<main::{lambda()#1}>::type std::__invoke<main::{lambda()#1}>(main::{lambda()#1}&&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009B69: void std::thread::_Invoker<std::tuple<main::{lambda()#1}> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009B11: std::thread::_Invoker<std::tuple<main::{lambda()#1}> >::operator()() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009ABF: std::thread::_State_impl<std::thread::_Invoker<std::tuple<main::{lambda()#1}> > >::_M_run() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==  Address 0x5dbbba0 is on thread [#2](/bitcoin-core-multiprocess/2/)'s stack
    ==276139==  in frame [#7](/bitcoin-core-multiprocess/7/), created by main::{lambda()#1}::operator()() const (???:)
    ==276139== 
    ==276139== 
    ==276139== ----------------------------------------------------------------
    ==276139== 
    ==276139== Possible data race during read of size 8 at 0x65BCC88 by thread [#2](/bitcoin-core-multiprocess/2/)
    ==276139== Locks held: none
    ==276139==    at 0x401ACD6: kj::Own<kj::Function<void ()>::Iface, decltype(nullptr)>::operator*() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x40181A9: kj::Function<void ()>::operator()() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4018049: void mp::Unlock<mp::Lock, kj::Function<void ()>&>(mp::Lock&, kj::Function<void ()>&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4010C5C: mp::EventLoop::loop() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4008ACC: main::{lambda()#1}::operator()() const (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009C6D: void std::__invoke_impl<void, main::{lambda()#1}>(std::__invoke_other, main::{lambda()#1}&&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009BF3: std::__invoke_result<main::{lambda()#1}>::type std::__invoke<main::{lambda()#1}>(main::{lambda()#1}&&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009B69: void std::thread::_Invoker<std::tuple<main::{lambda()#1}> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009B11: std::thread::_Invoker<std::tuple<main::{lambda()#1}> >::operator()() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009ABF: std::thread::_State_impl<std::thread::_Invoker<std::tuple<main::{lambda()#1}> > >::_M_run() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4CB95A3: ??? (in /usr/lib/libstdc++.so.6.0.34)
    ==276139==    by 0x503B98A: ??? (in /usr/lib/libc.so.6)
    ==276139==  Address 0x65bcc88 is on thread [#3](/bitcoin-core-multiprocess/3/)'s stack
    ==276139==  in frame [#7](/bitcoin-core-multiprocess/7/), created by main::{lambda()#2}::operator()() const (???:)
    ==276139== 
    ==276139== ----------------------------------------------------------------
    ==276139== 
    ==276139== Possible data race during read of size 8 at 0x51CF080 by thread [#2](/bitcoin-core-multiprocess/2/)
    ==276139== Locks held: none
    ==276139==    at 0x40181AA: kj::Function<void ()>::operator()() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4018049: void mp::Unlock<mp::Lock, kj::Function<void ()>&>(mp::Lock&, kj::Function<void ()>&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4010C5C: mp::EventLoop::loop() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4008ACC: main::{lambda()#1}::operator()() const (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009C6D: void std::__invoke_impl<void, main::{lambda()#1}>(std::__invoke_other, main::{lambda()#1}&&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009BF3: std::__invoke_result<main::{lambda()#1}>::type std::__invoke<main::{lambda()#1}>(main::{lambda()#1}&&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009B69: void std::thread::_Invoker<std::tuple<main::{lambda()#1}> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009B11: std::thread::_Invoker<std::tuple<main::{lambda()#1}> >::operator()() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009ABF: std::thread::_State_impl<std::thread::_Invoker<std::tuple<main::{lambda()#1}> > >::_M_run() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4CB95A3: ??? (in /usr/lib/libstdc++.so.6.0.34)
    ==276139==    by 0x503B98A: ??? (in /usr/lib/libc.so.6)
    ==276139==    by 0x50BF833: clone (in /usr/lib/libc.so.6)
    ==276139==  Address 0x51cf080 is 0 bytes inside a block of size 16 alloc'd
    ==276139==    at 0x488C093: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
    ==276139==    by 0x400929E: kj::Own<kj::Function<void ()>::Impl<main::{lambda()#2}::operator()() const::{lambda()#1}>, decltype(nullptr)> kj::heap<kj::Function<void ()>::Impl<main::{lambda()#2}::operator()() const::{lambda()#1}>, main::{lambda()#2}::operator()() const::{lambda()#1}>(main::{lambda()#2}::operator()() const::{lambda()#1}&&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4008FE9: kj::Function<void ()>::Function<main::{lambda()#2}::operator()() const::{lambda()#1}>(main::{lambda()#2}::operator()() const::{lambda()#1}&&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4008C66: main::{lambda()#2}::operator()() const (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009C30: void std::__invoke_impl<void, main::{lambda()#2}>(std::__invoke_other, main::{lambda()#2}&&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009BAE: std::__invoke_result<main::{lambda()#2}>::type std::__invoke<main::{lambda()#2}>(main::{lambda()#2}&&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009B3D: void std::thread::_Invoker<std::tuple<main::{lambda()#2}> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009AF5: std::thread::_Invoker<std::tuple<main::{lambda()#2}> >::operator()() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009A7F: std::thread::_State_impl<std::thread::_Invoker<std::tuple<main::{lambda()#2}> > >::_M_run() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4CB95A3: ??? (in /usr/lib/libstdc++.so.6.0.34)
    ==276139==    by 0x503B98A: ??? (in /usr/lib/libc.so.6)
    ==276139==    by 0x50BF833: clone (in /usr/lib/libc.so.6)
    ==276139==  Block was alloc'd by thread [#3](/bitcoin-core-multiprocess/3/)
    ==276139== 
    ==276139== ----------------------------------------------------------------
    ==276139== 
    ==276139== Thread [#2](/bitcoin-core-multiprocess/2/): Bug in libpthread: write lock granted on mutex/rwlock which is currently wr-held by a different thread
    ==276139==    at 0x4894C2A: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
    ==276139==    by 0x4014C11: std::mutex::lock() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4016614: std::unique_lock<std::mutex>::lock() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4014E69: mp::Lock::lock() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x401A456: mp::UnlockGuard<mp::Lock>::~UnlockGuard() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4018075: void mp::Unlock<mp::Lock, kj::Function<void ()>&>(mp::Lock&, kj::Function<void ()>&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4010C5C: mp::EventLoop::loop() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4008ACC: main::{lambda()#1}::operator()() const (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009C6D: void std::__invoke_impl<void, main::{lambda()#1}>(std::__invoke_other, main::{lambda()#1}&&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009BF3: std::__invoke_result<main::{lambda()#1}>::type std::__invoke<main::{lambda()#1}>(main::{lambda()#1}&&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009B69: void std::thread::_Invoker<std::tuple<main::{lambda()#1}> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009B11: std::thread::_Invoker<std::tuple<main::{lambda()#1}> >::operator()() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139== 
    ==276139== ----------------------------------------------------------------
    ==276139== 
    ==276139== Thread [#2](/bitcoin-core-multiprocess/2/) unlocked lock at 0x5DBBBA0 currently held by thread [#3](/bitcoin-core-multiprocess/3/)
    ==276139==    at 0x4895369: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
    ==276139==    by 0x4014C5D: std::mutex::unlock() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x40165C1: std::unique_lock<std::mutex>::unlock() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x401657D: std::unique_lock<std::mutex>::~unique_lock() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4015D49: mp::Lock::~Lock() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4011005: mp::EventLoop::loop() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4008ACC: main::{lambda()#1}::operator()() const (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009C6D: void std::__invoke_impl<void, main::{lambda()#1}>(std::__invoke_other, main::{lambda()#1}&&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009BF3: std::__invoke_result<main::{lambda()#1}>::type std::__invoke<main::{lambda()#1}>(main::{lambda()#1}&&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009B69: void std::thread::_Invoker<std::tuple<main::{lambda()#1}> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009B11: std::thread::_Invoker<std::tuple<main::{lambda()#1}> >::operator()() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009ABF: std::thread::_State_impl<std::thread::_Invoker<std::tuple<main::{lambda()#1}> > >::_M_run() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==  Lock at 0x5DBBBA0 was first observed
    ==276139==    at 0x4894C2A: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
    ==276139==    by 0x4014C11: std::mutex::lock() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4016614: std::unique_lock<std::mutex>::lock() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x401654E: std::unique_lock<std::mutex>::unique_lock(std::mutex&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4014E32: mp::Lock::Lock(mp::Mutex&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4010A4B: mp::EventLoop::loop() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4008ACC: main::{lambda()#1}::operator()() const (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009C6D: void std::__invoke_impl<void, main::{lambda()#1}>(std::__invoke_other, main::{lambda()#1}&&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009BF3: std::__invoke_result<main::{lambda()#1}>::type std::__invoke<main::{lambda()#1}>(main::{lambda()#1}&&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009B69: void std::thread::_Invoker<std::tuple<main::{lambda()#1}> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009B11: std::thread::_Invoker<std::tuple<main::{lambda()#1}> >::operator()() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009ABF: std::thread::_State_impl<std::thread::_Invoker<std::tuple<main::{lambda()#1}> > >::_M_run() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==  Address 0x5dbbba0 is on thread [#2](/bitcoin-core-multiprocess/2/)'s stack
    ==276139==  in frame [#6](/bitcoin-core-multiprocess/6/), created by main::{lambda()#1}::operator()() const (???:)
    ==276139== 
    ==276139== 
    loop() threw: throw from posted fn
    ==276139== ----------------------------------------------------------------
    ==276139== 
    ==276139==  Lock at 0x5DBBBA0 was first observed
    ==276139==    at 0x4894C2A: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
    ==276139==    by 0x4014C11: std::mutex::lock() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4016614: std::unique_lock<std::mutex>::lock() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x401654E: std::unique_lock<std::mutex>::unique_lock(std::mutex&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4014E32: mp::Lock::Lock(mp::Mutex&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4010A4B: mp::EventLoop::loop() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4008ACC: main::{lambda()#1}::operator()() const (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009C6D: void std::__invoke_impl<void, main::{lambda()#1}>(std::__invoke_other, main::{lambda()#1}&&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009BF3: std::__invoke_result<main::{lambda()#1}>::type std::__invoke<main::{lambda()#1}>(main::{lambda()#1}&&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009B69: void std::thread::_Invoker<std::tuple<main::{lambda()#1}> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009B11: std::thread::_Invoker<std::tuple<main::{lambda()#1}> >::operator()() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009ABF: std::thread::_State_impl<std::thread::_Invoker<std::tuple<main::{lambda()#1}> > >::_M_run() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==  Address 0x5dbbba0 is on thread [#2](/bitcoin-core-multiprocess/2/)'s stack
    ==276139==  in frame [#2](/bitcoin-core-multiprocess/2/), created by main::{lambda()#1}::operator()() const (???:)
    ==276139== 
    ==276139== Possible data race during read of size 8 at 0x5DBBB68 by thread [#2](/bitcoin-core-multiprocess/2/)
    ==276139== Locks held: none
    ==276139==    at 0x4017725: kj::_::DebugComparison<kj::Function<void ()>*&, decltype(nullptr)> kj::_::DebugExpression<kj::Function<void ()>*&>::operator==<decltype(nullptr)>(decltype(nullptr)&&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x40104BF: mp::EventLoop::~EventLoop() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4008ADB: main::{lambda()#1}::operator()() const (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009C6D: void std::__invoke_impl<void, main::{lambda()#1}>(std::__invoke_other, main::{lambda()#1}&&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009BF3: std::__invoke_result<main::{lambda()#1}>::type std::__invoke<main::{lambda()#1}>(main::{lambda()#1}&&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009B69: void std::thread::_Invoker<std::tuple<main::{lambda()#1}> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009B11: std::thread::_Invoker<std::tuple<main::{lambda()#1}> >::operator()() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009ABF: std::thread::_State_impl<std::thread::_Invoker<std::tuple<main::{lambda()#1}> > >::_M_run() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4CB95A3: ??? (in /usr/lib/libstdc++.so.6.0.34)
    ==276139==    by 0x503B98A: ??? (in /usr/lib/libc.so.6)
    ==276139==    by 0x50BF833: clone (in /usr/lib/libc.so.6)
    ==276139== 
    ==276139== This conflicts with a previous write of size 8 by thread [#3](/bitcoin-core-multiprocess/3/)
    ==276139== Locks held: 1, at address 0x5DBBBA0
    ==276139==    at 0x40112D9: mp::EventLoop::post(kj::Function<void ()>) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4008C75: main::{lambda()#2}::operator()() const (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009C30: void std::__invoke_impl<void, main::{lambda()#2}>(std::__invoke_other, main::{lambda()#2}&&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009BAE: std::__invoke_result<main::{lambda()#2}>::type std::__invoke<main::{lambda()#2}>(main::{lambda()#2}&&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009B3D: void std::thread::_Invoker<std::tuple<main::{lambda()#2}> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009AF5: std::thread::_Invoker<std::tuple<main::{lambda()#2}> >::operator()() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009A7F: std::thread::_State_impl<std::thread::_Invoker<std::tuple<main::{lambda()#2}> > >::_M_run() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4CB95A3: ??? (in /usr/lib/libstdc++.so.6.0.34)
    ==276139==  Address 0x5dbbb68 is on thread [#2](/bitcoin-core-multiprocess/2/)'s stack
    ==276139==  in frame [#2](/bitcoin-core-multiprocess/2/), created by main::{lambda()#1}::operator()() const (???:)
    ==276139== 
    terminate called after throwing an instance of 'kj::ExceptionImpl'
      what():  mp/proxy.cpp:216: failed: expected m_post_fn == nullptr [65bcc80 == nullptr]
    stack: 401052d 4008adb 4009c6d 4009bf3 4009b69 4009b11 4009abf 4cb95a3 503b98a 50bf833
    ==276139== 
    ==276139== Process terminating with default action of signal 6 (SIGABRT): dumping core
    ==276139==    at 0x503D90C: ??? (in /usr/lib/libc.so.6)
    ==276139==    by 0x4FE339F: raise (in /usr/lib/libc.so.6)
    ==276139==    by 0x4FCA579: abort (in /usr/lib/libc.so.6)
    ==276139==    by 0x4C6BBF5: ??? (in /usr/lib/libstdc++.so.6.0.34)
    ==276139==    by 0x4C85EB9: ??? (in /usr/lib/libstdc++.so.6.0.34)
    ==276139==    by 0x4C6B5D8: std::terminate() (in /usr/lib/libstdc++.so.6.0.34)
    ==276139==    by 0x4C86175: __cxa_throw (in /usr/lib/libstdc++.so.6.0.34)
    ==276139==    by 0x4B65249: ??? (in /usr/lib/libkj.so.1.2.0)
    ==276139==    by 0x4B713E2: kj::throwFatalException(kj::Exception&&, unsigned int) (in /usr/lib/libkj.so.1.2.0)
    ==276139==    by 0x4B7146A: kj::_::Debug::Fault::fatal() (in /usr/lib/libkj.so.1.2.0)
    ==276139==    by 0x401052D: mp::EventLoop::~EventLoop() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4008ADB: main::{lambda()#1}::operator()() const (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139== ----------------------------------------------------------------
    ==276139== 
    ==276139== Thread [#3](/bitcoin-core-multiprocess/3/): Exiting thread still holds 1 lock
    ==276139==    at 0x5043FE2: ??? (in /usr/lib/libc.so.6)
    ==276139==    by 0x503816B: ??? (in /usr/lib/libc.so.6)
    ==276139==    by 0x50387DB: ??? (in /usr/lib/libc.so.6)
    ==276139==    by 0x503AE9D: pthread_cond_wait (in /usr/lib/libc.so.6)
    ==276139==    by 0x4CAEEA0: std::condition_variable::wait(std::unique_lock<std::mutex>&) (in /usr/lib/libstdc++.so.6.0.34)
    ==276139==    by 0x4013150: void std::condition_variable::wait<mp::EventLoop::post(kj::Function<void ()>)::{lambda()#3}>(std::unique_lock<std::mutex>&, mp::EventLoop::post(kj::Function<void ()>)::{lambda()#3}) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4011323: mp::EventLoop::post(kj::Function<void ()>) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4008C75: main::{lambda()#2}::operator()() const (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009C30: void std::__invoke_impl<void, main::{lambda()#2}>(std::__invoke_other, main::{lambda()#2}&&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009BAE: std::__invoke_result<main::{lambda()#2}>::type std::__invoke<main::{lambda()#2}>(main::{lambda()#2}&&) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009B3D: void std::thread::_Invoker<std::tuple<main::{lambda()#2}> >::_M_invoke<0ul>(std::_Index_tuple<0ul>) (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139==    by 0x4009AF5: std::thread::_Invoker<std::tuple<main::{lambda()#2}> >::operator()() (in /home/mohamed/dev/libmultiprocess/build/test/post_race_test)
    ==276139== 
    ==276139== 
    ==276139== Use --history-level=approx or =none to gain increased speed, at
    ==276139== the cost of reduced accuracy of conflicting-access information
    ==276139== For lists of detected and suppressed errors, rerun with: -s
    ==276139== ERROR SUMMARY: 13 errors from 12 contexts (suppressed: 30 from 22)
    [1]    276139 IOT instruction (core dumped)  valgrind --tool=helgrind ./test/post_race_test
    
    
  2. Sjors commented at 1:06 PM on March 20, 2026: member

    I assume #249 doesn't (accidentally) prevent this?

  3. hulxv commented at 9:44 PM on March 20, 2026: contributor

    I assume #249 doesn't (accidentally) prevent this?

    no, it doesn't. it solves different issues. the same deadlocks happen when I tried it with its changes.

  4. ryanofsky commented at 7:03 PM on March 23, 2026: collaborator

    I'm not sure this is a bug exactly, but I think it's possible behavior could be improved here. Left some suggestions in the linked PR #260.

  5. ryanofsky closed this on Apr 14, 2026


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-04-20 17:30 UTC

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