test: cover immediate client disconnects for `ListenConnections` #310

pull xyzconstant wants to merge 2 commits into bitcoin-core:master from xyzconstant:server-disconnect-tests changing 1 files +44 −6
  1. xyzconstant commented at 12:42 AM on July 17, 2026: contributor

    Following on testing the reversed direction @ryanofsky suggested in #298, this PR adds a test to cover immediate client disconnects on the server side.

    Additionally, this test adds DefaultLogHandler and a log_handler parameter to ListenSetup, allowing individual tests to observe logs by passing a custom log handler. The new test takes advantage of this by catching and skipping Uncaught exception in daemonized task. logs.

    NOTE: an issue surfaced on the macOS job, the accept() call in ListenConnections fails for the closed connection, this is a Cap'n Proto bug as reported by @ViniciusCestarii in #310#pullrequestreview-4723481031 and its fix is available in the v2 branch.

  2. DrahtBot commented at 12:42 AM on July 17, 2026: none

    <!--e57a25ab6845829454e8d69fc972939a-->

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

    <!--021abf342d371248e50ceaed478a90ca-->

    Reviews

    See the guideline and AI policy for information on the review process.

    Type Reviewers
    Stale ACK ViniciusCestarii

    If your review is incorrectly listed, please copy-paste <code>&lt;!--meta-tag:bot-skip--&gt;</code> into the comment that the bot should ignore.

    <!--5faf32d7da4f0f540f40219e4f7537a3-->

  3. xyzconstant renamed this:
    ListenConnections: keep listening after a failed accept
    test: add coverage for immediate client disconnects in `ListenConnections`
    on Jul 17, 2026
  4. xyzconstant commented at 1:09 AM on July 17, 2026: contributor

    the macos job failed with:

    [ TEST ] listen_tests.cpp:292: ListenConnections survives a client that disconnects before being accepted
    mp/proxy.cpp:48: error: Uncaught exception in daemonized task.; exception = kj/async-io-unix.c++:1365: failed: setsocketopt(IPPROTO_TCP, TCP_NODELAY): Invalid argument
    stack: 1020dc7cb 1020dcaff 1020dcc60 10079556c
    /Users/runner/work/libmultiprocess/libmultiprocess/test/mp/test/listen_tests.cpp:189: failed: expected matched
    stack: 10078b28b 1007ac7bf
    

    I came across the same issue on my machine (MacBook Pro M2) too. I'll push a fix in a moment.

  5. xyzconstant force-pushed on Jul 17, 2026
  6. xyzconstant renamed this:
    test: add coverage for immediate client disconnects in `ListenConnections`
    Keep listening after a failed accept() in `ListenConnections`
    on Jul 17, 2026
  7. xyzconstant marked this as ready for review on Jul 17, 2026
  8. in include/mp/proxy-io.h:909 in 38391ebc19 outdated
     901 | @@ -902,6 +902,12 @@ void _Listen(const std::shared_ptr<Listener>& listener, EventLoop& loop, InitImp
     902 |                  if (resume_accept) _Listen<InitInterface>(listener, loop, init);
     903 |              });
     904 |              _Listen<InitInterface>(listener, loop, init);
     905 | +        },
     906 | +        // Keep listening if a single accept() fails, so the server does not
     907 | +        // stop accepting future connections.
     908 | +        [&loop, &init, listener](const kj::Exception& e) {
     909 | +            MP_LOG(loop, Log::Info) << "IPC server: accept failed.";
    


    ViniciusCestarii commented at 2:19 PM on July 17, 2026:

    In "Keep listening after a failed accept in ListenConnections" 38391ebc196d7ece7152a6372993ba104dceb9c6

    nit: I believe MP_LOG(loop, Log::Warning) would fit better here and also the kj exception could be logged too


    xyzconstant commented at 4:06 PM on July 17, 2026:

    Solved at d8e5395ed8e6f21fc847b65148c2ec3c34bce524 (compare)

    Thanks!

  9. ViniciusCestarii commented at 3:01 PM on July 17, 2026: contributor

    ACK 38391ebc196d7ece7152a6372993ba104dceb9c6 Nice finding! This is nicer than the old code that would just stop listening because of a transient error that KJ didn't catch but also with this change now a non-transient error will spin forever retrying an unrecoverable error which I believe could be fixed in a follow up.

    Also I have dug in and I found that this is a KJ bug because it should treat transient errors like this and it was already fixed for capnp v2 which isn't released yet.

    Given that, I still think this PR is correct and the defensive retry is the right call regardless. The spin forever on permanent failure case remains a valid follow-up on top of this.

  10. in test/mp/test/listen_tests.cpp:299 in 38391ebc19 outdated
     294 | +    ListenSetup server;
     295 | +
     296 | +    // Connect and close before the server has a chance to accept() the
     297 | +    // connection.
     298 | +    int fd = server.listener.MakeConnectedSocket();
     299 | +    close(fd);
    


    ryanofsky commented at 3:37 PM on July 17, 2026:

    In commit "Keep listening after a failed accept in ListenConnections" (38391ebc196d7ece7152a6372993ba104dceb9c6)

    Can this be checked with KJ_SYSCALL?


    ryanofsky commented at 4:10 PM on July 17, 2026:

    In commit "Keep listening after a failed accept in ListenConnections" (d8e5395ed8e6f21fc847b65148c2ec3c34bce524)

    Would be good to add a comment here that this close is a little racy, if the socket not closed before accepted the test could appear to crash when there is still a problem.

  11. xyzconstant force-pushed on Jul 17, 2026
  12. xyzconstant commented at 4:05 PM on July 17, 2026: contributor

    Thanks for the review @ViniciusCestarii!

    Also I have dug in and I found that this is a KJ bug because it should treat transient errors like this and it was already fixed for capnp v2 which isn't released yet.

    Nice catch! Now it's clear why this wasn't failing on other platforms.

    Given that, I still think this PR is correct and the defensive retry is the right call regardless. The spin forever on permanent failure case remains a valid follow-up on top of this.

    You're right on this, I was considering this too. I'm yet to figure out which exceptions we want to stop the accept loop for and which we want to retry, so a follow-up is the right call to me. If other reviewers would rather it be solved in this PR, I'm happy to tackle it here though.

  13. xyzconstant force-pushed on Jul 17, 2026
  14. ryanofsky commented at 4:12 PM on July 17, 2026: collaborator

    Code review 38391ebc196d7ece7152a6372993ba104dceb9c6

    Nice catch and elegant test! However, I think the suggested fix is too dangerous because retrying whenever accept fails could lead to an infinite loop if the accept failed for reasons other than this macos/capnproto bug, like file descriptors being closed, or resources being exhausted.

    I think it'd be good to add the test but not the fix here, and make the test allow the setsocketopt(IPPROTO_TCP, TCP_NODELAY): Invalid argument error (https://github.com/bitcoin-core/libmultiprocess/actions/runs/29545518652/job/87776862017#step:4:126) that results on macos when the capnproto version is less than whatever capnproto version fixes this to document the bug. Separately we should make sure this is fixed in the capnproto version we are using in Bitcoin Core and consider adding a patch to the Bitcoin Core depends build if needed temporarily.

  15. ViniciusCestarii commented at 5:21 PM on July 17, 2026: contributor

    Thinking back here, I agree with @ryanofsky. In my review I understimated the spin forever on permanent failure case and the fix truly belongs to KJ and not here because if we were to try to reliably distinguish a transient error from a non transient error here we would basically be doing what KJ was supposed to do

  16. test: allow custom log handler in `ListenSetup`
    Add `DefaultLogHandler` and a `log_handler` parameter so tests can provide
    their own handler and observe log output more easily.
    140d9ba6ff
  17. xyzconstant force-pushed on Jul 20, 2026
  18. xyzconstant renamed this:
    Keep listening after a failed accept() in `ListenConnections`
    test: cover immediate client disconnects for `ListenConnections`
    on Jul 20, 2026
  19. test: cover immediate client disconnects for `ListenConnections`
    Connect to the listening socket and `close()` the connection right away,
    note that this is racy and the connection might be accepted normally if
    the `close()` syscall doesn't win the race.
    
    Additionally, a custom log handler is added to catch "Uncaught exception
    in daemonized task." logs, generated by a Cap'n Proto bug that shows up
    on macOS, so CI does not fail on them.
    b5b357f22a
  20. xyzconstant force-pushed on Jul 20, 2026
  21. xyzconstant commented at 6:58 PM on July 20, 2026: contributor

    Thanks for the reviews, addressed feedback and force-pushed: d8e5395ed8e6f21fc847b65148c2ec3c34bce524 -> b5b357f22a37a59dd019caebe283b92eeb3b76c6 (compare)

    re: #310#pullrequestreview-4724053865

    I think it'd be good to add the test but not the fix here, and make the test allow the setsocketopt(IPPROTO_TCP, TCP_NODELAY): Invalid argument error (https://github.com/bitcoin-core/libmultiprocess/actions/runs/29545518652/job/87776862017#step:4:126) that results on macos when the capnproto version is less than whatever capnproto version fixes this to document the bug. Separately we should make sure this is fixed in the capnproto version we are using in Bitcoin Core and consider adding a patch to the Bitcoin Core depends build if needed temporarily.

    Fair concern and I think you're right. The fix was designed to pass the test, assuming this behavior would be tolerated. So it's better we drop it, even though it means the test has a workaround for this macOS bug, which I think we could be okay with.

  22. xyzconstant commented at 11:24 PM on July 23, 2026: contributor

    Just opened #319 to keep track of the macOS accept crash.


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-07-27 09:30 UTC

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