refactor: EventLoop locking cleanups + client disconnect exception #160

pull ryanofsky wants to merge 13 commits into bitcoin-core:master from ryanofsky:pr/eventlock changing 9 files +447 −219
  1. ryanofsky commented at 5:17 am on February 10, 2025: collaborator

    This PR was originally written as a code cleanup to follow up on review comments in earlier PRs. Several other commits were added afterward building on the earlier changes, and necessary for the bugfixes in https://github.com/bitcoin/bitcoin/pull/32345, which resolves issues #123, #176 and #182.

    Summary of changes:

    • The commit “Improve IPC client disconnected exceptions” is the only external change, and lets clients detect when they are trying to call a remote method after a connection has been closed. This change is used by bitcoin/bitcoin#32345 commit “ipc: Handle bitcoin-wallet disconnection” to fix #123.
    • The commits “Prevent EventLoop async cleanup thread early exit during shutdown” and “Prevent IPC server crash if disconnected during IPC call” fix issues reported in #182.
    • New tests are added in other commits to verify these fixes, covering different kinds of disconnections.
    • The change to detect disconnects also relies on an earlier “Add ProxyContext EventLoop* member” commit.
    • An “Add EventLoopRef RAII class” commit simplifies a recent bugfix in #159 and also relies on the earlier “EventLoop* member” commit.
    • The “Remove DestructorCatcher and AsyncCallable” commit follows up on #144 (comment).
    • The “Add clang thread safety annotations” commit follows up on #129 (comment).
  2. ryanofsky commented at 5:19 am on February 10, 2025: collaborator

    Because this change removes EventLoop addClient and removeClient methods it will require an update to Bitcoin core if it is merged.

     0--- a/src/ipc/capnp/protocol.cpp
     1+++ b/src/ipc/capnp/protocol.cpp
     2@@ -41,10 +41,7 @@ class CapnpProtocol : public Protocol
     3 public:
     4     ~CapnpProtocol() noexcept(true)
     5     {
     6-        if (m_loop) {
     7-            std::unique_lock<std::mutex> lock(m_loop->m_mutex);
     8-            m_loop->removeClient(lock);
     9-        }
    10+        m_loop_ref.reset();
    11         if (m_loop_thread.joinable()) m_loop_thread.join();
    12         assert(!m_loop);
    13     };
    14@@ -83,10 +80,7 @@ public:
    15         m_loop_thread = std::thread([&] {
    16             util::ThreadRename("capnp-loop");
    17             m_loop.emplace(exe_name, &IpcLogFn, &m_context);
    18-            {
    19-                std::unique_lock<std::mutex> lock(m_loop->m_mutex);
    20-                m_loop->addClient(lock);
    21-            }
    22+            m_loop_ref.emplace(*m_loop);
    23             promise.set_value();
    24             m_loop->loop();
    25             m_loop.reset();
    26@@ -96,6 +90,7 @@ public:
    27     Context m_context;
    28     std::thread m_loop_thread;
    29     std::optional<mp::EventLoop> m_loop;
    30+    std::optional<mp::EventLoopRef> m_loop_ref;
    31 };
    32 } // namespace
    
  3. ryanofsky renamed this:
    refactor: EventLoop locking cleanups
    refactor: EventLoop locking cleanups and client Disconnect errors
    on Apr 24, 2025
  4. ryanofsky renamed this:
    refactor: EventLoop locking cleanups and client Disconnect errors
    refactor: EventLoop locking cleanups + client disconnect exceptions
    on Apr 24, 2025
  5. ryanofsky force-pushed on Apr 24, 2025
  6. ryanofsky renamed this:
    refactor: EventLoop locking cleanups + client disconnect exceptions
    refactor: EventLoop locking cleanups + client disconnect exception
    on Apr 24, 2025
  7. ryanofsky commented at 3:09 pm on April 24, 2025: collaborator
    Updated ce4814f46d3c38d0bdd08df2253bc69b084f22ee -> b47ea9f69307908680595b96720bf41464bb3089 (pr/eventlock.1 -> pr/eventlock.2, compare), making test and comment fixes, splitting commits, and adding connection disconnects tests and exceptions to help resolve #123
  8. ryanofsky referenced this in commit 168144fcb9 on Apr 24, 2025
  9. ryanofsky referenced this in commit e8372fa3a6 on Apr 24, 2025
  10. ryanofsky force-pushed on Apr 24, 2025
  11. ryanofsky commented at 9:03 pm on April 24, 2025: collaborator
    Rebased b47ea9f69307908680595b96720bf41464bb3089 -> f15ef6cdeec54c83f52e00de47d57b09f9a5f03b (pr/eventlock.2 -> pr/eventlock.3, compare) so this can be merged cleanly in bitcoin PR https://github.com/bitcoin/bitcoin/pull/32345. Also dropped DisconnectError exception type so original ipc::Exception can be used instead.
  12. ryanofsky referenced this in commit 197b2aaaaa on Apr 25, 2025
  13. ryanofsky referenced this in commit a56468ab0b on Apr 25, 2025
  14. ryanofsky referenced this in commit 0b72656ecf on Apr 28, 2025
  15. ryanofsky referenced this in commit 5b38e62ccb on Apr 28, 2025
  16. ryanofsky force-pushed on May 15, 2025
  17. ryanofsky commented at 7:29 pm on May 15, 2025: collaborator
    Rebased f15ef6cdeec54c83f52e00de47d57b09f9a5f03b -> 22143588944af415bd420eb0418b331d1fd3cd86 (pr/eventlock.3 -> pr/eventlock.4, compare) to fix conflict with #165
  18. ryanofsky force-pushed on May 30, 2025
  19. ryanofsky commented at 3:24 pm on May 30, 2025: collaborator
    Rebased 22143588944af415bd420eb0418b331d1fd3cd86 -> 6715a967d98c05a936a0ede153cc7c485d36590f (pr/eventlock.4 -> pr/eventlock.5, compare) to fix conflicts with #172
  20. ryanofsky referenced this in commit b6c703c543 on Jun 5, 2025
  21. ryanofsky referenced this in commit c1cce04f76 on Jun 5, 2025
  22. ryanofsky referenced this in commit e76ae54f2d on Jun 5, 2025
  23. ryanofsky referenced this in commit 6e3679d98b on Jun 5, 2025
  24. ryanofsky referenced this in commit a180915d36 on Jun 5, 2025
  25. ryanofsky referenced this in commit d2b1e821d4 on Jun 6, 2025
  26. ryanofsky commented at 5:45 pm on June 6, 2025: collaborator
    Rewrote the PR description since this is now a dependency of https://github.com/bitcoin/bitcoin/pull/32345 and needed to fix bugs #123 and #176
  27. in test/mp/test/test.cpp:73 in 91eb20c167 outdated
    66+              server_connection->onDisconnect([&] { server_connection.reset(); });
    67+
    68+              auto client_connection = std::make_unique<Connection>(loop, kj::mv(pipe.ends[1]));
    69+              auto client_proxy = std::make_unique<ProxyClient<messages::FooInterface>>(
    70+                  client_connection->m_rpc_system->bootstrap(ServerVatId().vat_id).castAs<messages::FooInterface>(),
    71+                  client_connection.get(), /* destroy_connection= */ client_owns_connection);
    


    Sjors commented at 11:38 am on June 9, 2025:

    In 91eb20c16791e605228ed956754f43afd10b4de9 test: Add test coverage for client & server disconnections: it would be good to document destroy_connection in the ProxyClientBase or elsewhere.

    I’m a bit confused why this commit changes it from false to client_owns_connection, which is true by default and in the original test.


    ryanofsky commented at 2:03 pm on June 9, 2025:

    re: #160 (review)

    it would be good to document destroy_connection in the ProxyClientBase or elsewhere.

    Thanks added a new commit with documentation for this.

    I’m a bit confused why this commit changes it from false to client_owns_connection, which is true by default and in the original test.

    I think it was just false in the original test, and the new test has variants setting it both true and false to add some coverage for both values.

    The default is true in the test because true simplifies the test cases slightly (they don’t have make explicit client_disconnect calls to delete the Connection pointer themselves).

    In the test code the destroy_connection option is called client_owns_connection because destroy_connection is a ProxyClientBase constructor argument, so the client_ prefix is already implied there. And owns_connection is more accurate than destroy_connection in the test because if the option is true, the client only destroys the connection if something else doesn’t destroy it first. Like if the connection is broken, the Connection object will already gone by the time the client is destroyed, because it will be destroyed by the onDisconnect handler.


    TheCharlatan commented at 12:07 pm on June 16, 2025:
    Might be good to expand the comment to say explicitly that setting it sets the client_disconnect callback.
  28. Sjors approved
  29. Sjors commented at 12:56 pm on June 9, 2025: member

    ACK 6715a967d98c05a936a0ede153cc7c485d36590f

    The changes here make sense to me, at least at a high level.

  30. ryanofsky referenced this in commit 0b45cb5662 on Jun 9, 2025
  31. ryanofsky commented at 2:10 pm on June 9, 2025: collaborator

    Thanks for the review!

    Added 1 commit 6715a967d98c05a936a0ede153cc7c485d36590f -> 0b45cb5662798553bc51979125d5c549d05e3262 (pr/eventlock.5 -> pr/eventlock.6, compare) documenting ProxyClientBase destroy_connection option

  32. test: Add test coverage for client & server disconnections 5108445e5d
  33. proxy-io.h: Add more detailed EventLoop comment f58c8d8ba2
  34. proxy-io.h: Add EventLoopRef RAII class handle addClient/removeClient refcounting 9aaeec3678
  35. refactor: Add ProxyContext EventLoop* member
    This commit makes mechanical changes needed to simplify an upcoming commit
    which replaces EventLoop* with an EventLoopRef.
    
    This change also happens to be also useful on its own so clientInvoke can
    detect disconnections in a non-racy way
    (https://github.com/bitcoin-core/libmultiprocess/issues/123#issuecomment-2516088243)
    by seeing if the client Connection pointer is null while holding the event loop
    mutex.
    315ff537fb
  36. refactor: Use EventLoopRef instead of addClient/removeClient
    Use EventLoopRef to avoid reference counting bugs and be more exception safe
    2b830e558e
  37. refactor: Drop addClient/removeClient methods
    Now that they are only called in one place by EventLoopRef class, they can be
    inlined.
    f24894794a
  38. refactor: Remove DestructorCatcher and AsyncCallable
    Use kj::Function instead of std::function to avoid the need for AsyncCallable
    and DestructorCatcher classes, which were used to work around the requirement
    that std::function objects need to be copyable. kj::Function does not have this
    requirement.
    
    Change is from https://github.com/chaincodelabs/libmultiprocess/pull/144#discussion_r1941178169
    
    Co-authored-by: Ryan Ofsky <ryan@ofsky.org>
    52256e730f
  39. refactor: Add clang thread safety annotations to EventLoop
    Add basic thread safety annotations to EventLoop. Use could be expanded and
    deepened but this puts basic functionality in place.
    
    Use of annotations was discussed in
    https://github.com/chaincodelabs/libmultiprocess/pull/129#discussion_r1928455500
    9b8ed3dc5f
  40. Improve IPC client disconnected exceptions
    Improve clientInvoke exceptions so IPC clients can more reliably detect when
    they are calling a remote method after the connection is closed. Before this
    change different exceptions were thrown, which made this condition difficult to
    detect and handle.
    56fff76f94
  41. doc: Document ProxyClientBase destroy_connection option
    Suggested by Sjors Provoost <sjors@sprovoost.nl>
    https://github.com/bitcoin-core/libmultiprocess/pull/160#discussion_r2135564583
    616d9a75d2
  42. Prevent EventLoop async cleanup thread early exit during shutdown
    Antoine Poinsot <darosior@protonmail.com> reported a bug with details in
    https://github.com/bitcoin-core/libmultiprocess/issues/182#issuecomment-2960280611
    where an IPC client rapidly connecting and disconnecting to the server in a
    loop could cause problems.
    
    One problem, fixed by this commit, was that if a server process is shutting
    down, the async cleanup thread in `EventLoop::startAsyncThread` responsible for
    destroying unused server object on unclean disconnects could detect the done()
    condition and decide to exit right before a new incoming connection is
    processed, and exit prematurely when there might be more cleanup work for it
    do. If this happens, process shutdown could hang waiting for cleanup work that
    will never be completed.
    
    This commit fixes that problem by changing the `EventLoop::startAsyncThread()`
    while condition to check whether the `EventLoop::loop() method has _exited_,
    instead of checking whether it is about to exit. Specifically the change makes
    the `m_async_fns` list into a `std::optional` value and sets it to `nullopt`
    when the `loop()` method exits.
    ea38392960
  43. Prevent IPC server crash if disconnected during IPC call
    Antoine Poinsot <darosior@protonmail.com> reported a bug with details in
    https://github.com/bitcoin-core/libmultiprocess/issues/182#issuecomment-2960280611
    where an IPC client rapidly connecting and disconnecting to the server in a
    loop could cause problems.
    
    One problem this uncovered was hangs on shutdown fixed by the previous commit.
    
    Another problem it uncovered is that if a disconnect happened while the IPC
    server was executing an IPC call, and the onDisconnect() handling code ran
    before the IPC call finished, memory corruption would occur usually leading to
    a hang or crash.
    
    This happend because the ~ProxyServerBase() destructor was written with the
    assumption that it would always be called either from the ~Connection()
    destructor, or before that point, so its m_context.connection pointer would
    always be valid. Typically this was the case, but not if the connection was
    closed during an active IPC call.
    
    A unit test to reproduce this error is added in the subsequent commit.
    
    The fix for the bug here is actually a code simplification.  Previously
    ~ProxyServerBase() destructor would append a std::function callback destroying
    the ProxyServerBase::m_impl object to
    m_context.connection->m_async_cleanup_fns, so the object destructor could be
    called asynchronously without blocking the event loop. Now it just appends the
    same callback function to m_context.loop->m_async_fns without going through the
    Connection object.
    
    The new code is an improvement over the previous code in two other ways:
    
    - It is a code simplification since previous code was needlessly complicated.
    
    - In the case where there is no disconnect, and the remote ProxyClient is
      destroyed, and no "destroy" method is declared, this change causes the
      ProxyServer::m_impl object to be freed shortly after the client is freed,
      instead of being delayed until the connection is closed. (If a "destroy"
      method is declared, this change has no effect because in that case destroying
      the ProxyClient calls destroy and destroys ProxyServer::m_impl synchronously.)
    
    The previous code was trying to make the ProxyServer cleanup with
    Connection::m_async_cleanup_fns mirror the ProxyClient cleanup with
    Connection::m_sync_cleanup_fns, but it was just fragile and unncecessarily
    complicated.
    
    Some comments about ProxyClient cleanup code are updated for clarity here but
    no changes are made.
    949573da84
  44. test: Test disconnects during IPC calls
    This change adds two unit tests to make sure shutdown happens cleanly if the
    the client disconnects while the server is processing an IPC call.
    
    The first test checks a synchronous case which has always worked, where the
    disconnect starts but is not processed by the event loop until after the IPC
    call completes.
    
    The second test checks an asynchronous case which was fixed by the previous
    commit, where the disconnect is processed before the IPC call generates a
    response. This test would usually crash or hang prior to the fix in the
    previous commit. It would crash reliably with address sanitizer.
    84cf56a0b5
  45. ryanofsky referenced this in commit ccbdff5282 on Jun 13, 2025
  46. ryanofsky force-pushed on Jun 13, 2025
  47. ryanofsky referenced this in commit dd92e847b0 on Jun 13, 2025
  48. ryanofsky commented at 7:43 pm on June 13, 2025: collaborator
    Rebased 0b45cb5662798553bc51979125d5c549d05e3262 -> 4c3373459d3a2abfa2c5e143f271ec8a4e25b346 (pr/eventlock.6 -> pr/eventlock.7, compare) due to conflict with #181, also adding 3 commits to fix #182, and making minor cleanups to earlier commits (mainly comments) Updated 4c3373459d3a2abfa2c5e143f271ec8a4e25b346 -> 2dcfb46e45ee39be62d4caff136b9a9412dfe5c4 (pr/eventlock.7 -> pr/eventlock.8, compare) to fix spurious thread safety warning https://cirrus-ci.com/task/4956347122319360 and spelling errors Updated 2dcfb46e45ee39be62d4caff136b9a9412dfe5c4 -> 84cf56a0b5f496b02e761dc8fb243cd6a69b3888 (pr/eventlock.8 -> pr/eventlock.9, compare) to fix spurious thread safety warning https://cirrus-ci.com/task/5066891695226880
  49. ryanofsky referenced this in commit f8d90c6fda on Jun 13, 2025
  50. ryanofsky referenced this in commit 9cdd86a4ca on Jun 13, 2025
  51. ryanofsky referenced this in commit 2a29223f52 on Jun 13, 2025
  52. ryanofsky referenced this in commit 49646814a4 on Jun 13, 2025
  53. ryanofsky referenced this in commit 3317dc4e86 on Jun 13, 2025
  54. ryanofsky referenced this in commit 51868ebb42 on Jun 13, 2025
  55. ryanofsky force-pushed on Jun 16, 2025
  56. ryanofsky referenced this in commit 8b897f4542 on Jun 16, 2025
  57. ryanofsky referenced this in commit f0d6a9b10c on Jun 16, 2025
  58. ryanofsky referenced this in commit 4cecb0160f on Jun 16, 2025
  59. ryanofsky referenced this in commit 616a09631f on Jun 16, 2025
  60. ryanofsky force-pushed on Jun 16, 2025
  61. ryanofsky referenced this in commit 64da8e2c8c on Jun 16, 2025
  62. ryanofsky referenced this in commit 67598adbed on Jun 16, 2025
  63. ryanofsky referenced this in commit d234efe92e on Jun 16, 2025
  64. ryanofsky referenced this in commit 53b12cac6b on Jun 16, 2025
  65. in include/mp/proxy.h:58 in 9aaeec3678 outdated
    53+//! otherwise EventLoop::m_mutex will be locked when needed.
    54+class EventLoopRef
    55+{
    56+public:
    57+    explicit EventLoopRef(EventLoop& loop, std::unique_lock<std::mutex>* lock = nullptr);
    58+    EventLoopRef(EventLoopRef&& other) noexcept : m_loop(other.m_loop) { other.m_loop = nullptr; }
    


    TheCharlatan commented at 12:26 pm on June 16, 2025:
    As far as I can tell it is correct to not copy over the lock from the moved object here as long as the moved object has no lock. Should that be asserted?

    ryanofsky commented at 10:20 pm on June 16, 2025:

    re: #160 (review)

    In commit “proxy-io.h: Add EventLoopRef RAII class handle addClient/removeClient refcounting” (9aaeec3678d3b5dad61d2142c37bac18aa6257e0)

    As far as I can tell it is correct to not copy over the lock from the moved object here as long as the moved object has no lock. Should that be asserted?

    It’s kind of a corner case, and I think either behavior could be useful here. The point of this constructor is to be able to efficiently move a reference count from one owner to another would pointlessly incrementing & decrement the count when creating one reference and dropping the other.

    I feel like it probably would not be useful to copy the m_lock pointer when doing that because practical effect of copying would be to make the new EventLoopRef object destructor skip locking when it is destroyed (it would assert m_lock is locked instead).

    On the other hand the default move-constructor would copy the m_lock value so maybe it is less surprising to follow the default behavior. And if the caller really wanted m_lock to be null after moving they could set it to null themselves.

    So this is a good catch and I think I’d now lean towards adding m_lock{other.m_lock} but I don’t think the difference should matter in practice.

    More broadly I was also thinking about whether I could split EventLoopRef into a superclass without an m_lock member and a subclass with one to be able to get rid of the relock option added in ea38392960e1edbdb423bebc5cc0a665fa83a253 which requires turning off thread safety analysis in the reset function. At the moment this class seems good at preventing reference counting bugs because it guarantees reference counts will always be decremented if they are incremented, but it doesn’t have very easy to understand locking semantics so that’s probably an area for improvement.

  66. in src/mp/proxy.cpp:291 in ea38392960 outdated
    289         m_async_thread = std::thread([this] {
    290             Lock lock(m_mutex);
    291-            while (!done()) {
    292-                if (!m_async_fns.empty()) {
    293+            while (m_async_fns) {
    294+                if (!m_async_fns->empty()) {
    


    TheCharlatan commented at 7:44 pm on June 16, 2025:
    Is there a chance of encountering a race condition here between checking m_async_fns and calling ->empty()?

    ryanofsky commented at 10:28 pm on June 16, 2025:

    re: #160 (review)

    In commit “Prevent EventLoop async cleanup thread early exit during shutdown” (ea38392960e1edbdb423bebc5cc0a665fa83a253)

    Is there a chance of encountering a race condition here between checking m_async_fns and calling ->empty()?

    There shouldn’t be just because m_async_fns is guarded by m_mutex which is locked above.

  67. in test/mp/test/test.cpp:220 in 84cf56a0b5
    216@@ -215,5 +217,69 @@ KJ_TEST("Calling IPC method after server connection is closed")
    217     KJ_EXPECT(disconnected);
    218 }
    219 
    220+KJ_TEST("Calling IPC method and disconnecting during the call")
    


    TheCharlatan commented at 8:12 pm on June 16, 2025:

    The previous test exercises a server disconnect. Would it be useful to either add another case, or expand it to test the server going away during a client call? I tried just setting

    0    setup.server->m_impl->m_fn = setup.server_disconnect;
    

    but that crashes, which I guess is kind of expected, since we’re tearing down objects that are used in the server thread.


    ryanofsky commented at 10:43 pm on June 16, 2025:

    re: #160 (review)

    In commit “test: Test disconnects during IPC calls” (84cf56a0b5f496b02e761dc8fb243cd6a69b3888)

    The previous test exercises a server disconnect. Would it be useful to either add another case, or expand it to test the server going away during a client call?

    That’s a good idea. It should be legitimate for an IPC call on the server to close the connection which triggered the call, or for some other thread on the server to close the connection while a slow IPC call is executing. So it would make sense to add tests covering these cases and I’m a little surprised the change you tested doesn’t work.

    I should look into what the cause of the problem is but I suspect it is something simple like an unchecked null pointer or uncaught exception. I am beginning to learn that there are many different ways connections can be disconnected and interrupted and in capnproto each way tends to lead to a different piece of code throwing an exception. #183 is another case found today. It seems like I should probably add broader exception handlers instead of letting errors bubble up so far.

  68. TheCharlatan commented at 9:45 am on June 17, 2025: collaborator
    For what it’s worth I think the code here could be merged. Further improving exception handling does not all have to happen in here. Getting the EventLoop locking cleanups in is a clear improvement on the baseline in my eyes.
  69. ryanofsky commented at 12:23 pm on June 17, 2025: collaborator

    re: #160 (comment)

    For what it’s worth I think the code here could be merged. Further improving exception handling does not all have to happen in here. Getting the EventLoop locking cleanups in is a clear improvement on the baseline in my eyes.

    Yes that makes a lot of sense. There are more improvements to be made but I don’t think it’s good for this PR to keep growing forever just because the next set of improvements depend on current ones.

    So I’ll plan to not make any more changes here and merge in the next day or so, but I would appreciate if you or Sjors or other reviewers could look at this a little more and add acks if appropriate before then. As mentioned #172 (comment) I would really like to get away from the practice of merging PRs here without current acks.

  70. TheCharlatan approved
  71. TheCharlatan commented at 1:06 pm on June 17, 2025: collaborator
    ACK 84cf56a0b5f496b02e761dc8fb243cd6a69b3888
  72. ryanofsky merged this on Jun 19, 2025
  73. ryanofsky closed this on Jun 19, 2025

  74. Sjors referenced this in commit ebfb66188e on Jun 24, 2025
  75. Sjors referenced this in commit 35f9fa4ec6 on Jun 24, 2025
  76. ryanofsky referenced this in commit ac9c06f030 on Jun 24, 2025
  77. ryanofsky referenced this in commit 711b8e2f8f on Jun 24, 2025
  78. ryanofsky referenced this in commit 9b4ec43f78 on Jun 24, 2025
  79. ryanofsky referenced this in commit 963e997314 on Jun 24, 2025
  80. ryanofsky referenced this in commit 3ed3f7f979 on Jun 24, 2025
  81. Sjors referenced this in commit 063587c2de on Jun 27, 2025
  82. Sjors referenced this in commit dcb5439d45 on Jun 27, 2025
  83. Sjors referenced this in commit e5104ccc26 on Jun 27, 2025
  84. ryanofsky referenced this in commit 7455fc0e37 on Jul 1, 2025
  85. ryanofsky referenced this in commit fb58fcb9d5 on Jul 1, 2025
  86. ryanofsky referenced this in commit 868826b33d on Jul 1, 2025
  87. ryanofsky referenced this in commit 96bcbb60a6 on Jul 1, 2025
  88. ryanofsky referenced this in commit 9136e497ee on Jul 1, 2025
  89. ryanofsky referenced this in commit 5024f55292 on Jul 1, 2025
  90. ryanofsky referenced this in commit c6f7fdf173 on Jul 1, 2025
  91. ryanofsky referenced this in commit 6e40719c06 on Jul 1, 2025
  92. ryanofsky referenced this in commit 8eb8920b61 on Jul 1, 2025
  93. ryanofsky referenced this in commit b06a204dc0 on Jul 1, 2025
  94. ryanofsky referenced this in commit fd10adcb65 on Jul 1, 2025
  95. ryanofsky referenced this in commit 5cda260638 on Jul 1, 2025
  96. ryanofsky referenced this in commit d62ec01e3d on Jul 1, 2025
  97. ryanofsky referenced this in commit 7c381d2780 on Jul 1, 2025
  98. ryanofsky referenced this in commit a67f3b07d4 on Jul 1, 2025
  99. ryanofsky referenced this in commit a11e6905c2 on Jul 1, 2025
  100. ryanofsky referenced this in commit 08fde51bd4 on Jul 1, 2025
  101. ryanofsky referenced this in commit 16b9412031 on Jul 1, 2025
  102. ryanofsky referenced this in commit ca509dd2b9 on Jul 1, 2025
  103. ryanofsky referenced this in commit f8fd3959d5 on Jul 1, 2025
  104. ryanofsky referenced this in commit 0a9a194078 on Jul 1, 2025
  105. ryanofsky referenced this in commit 775935014b on Aug 8, 2025
  106. ryanofsky referenced this in commit 850e4ca7ed on Aug 8, 2025
  107. ryanofsky referenced this in commit e886c65b6b on Aug 8, 2025
  108. ryanofsky referenced this in commit 9a9fb19536 on Aug 8, 2025
  109. ryanofsky referenced this in commit 2581258ec2 on Aug 8, 2025
  110. Sjors referenced this in commit 8ce8288ee6 on Aug 12, 2025
  111. Sjors referenced this in commit 8cfeaaeb4b on Aug 12, 2025
  112. Sjors referenced this in commit c99c8b3fd7 on Aug 12, 2025
  113. Sjors referenced this in commit 1364b3c427 on Aug 12, 2025
  114. fanquake referenced this in commit f58de8749e on Aug 18, 2025
  115. janus referenced this in commit 9175096037 on Sep 15, 2025
  116. janus referenced this in commit b116536bac on Sep 15, 2025
  117. janus referenced this in commit d7ab135840 on Sep 15, 2025

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: 2025-12-04 19:30 UTC

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