Skip to content

test: cover immediate client disconnects for ListenConnections - #310

Merged
ryanofsky merged 2 commits into
bitcoin-core:masterfrom
xyzconstant:server-disconnect-tests
Aug 3, 2026
Merged

test: cover immediate client disconnects for ListenConnections#310
ryanofsky merged 2 commits into
bitcoin-core:masterfrom
xyzconstant:server-disconnect-tests

Conversation

@xyzconstant

@xyzconstant xyzconstant commented Jul 17, 2026

Copy link
Copy Markdown
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 (review) and its fix is available in the v2 branch.

@DrahtBot

DrahtBot commented Jul 17, 2026

Copy link
Copy Markdown

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

Reviews

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

Type Reviewers
ACK ryanofsky
Stale ACK ViniciusCestarii

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

@xyzconstant xyzconstant changed the title ListenConnections: keep listening after a failed accept test: add coverage for immediate client disconnects in ListenConnections Jul 17, 2026
@xyzconstant

Copy link
Copy Markdown
Contributor Author

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.

@xyzconstant
xyzconstant force-pushed the server-disconnect-tests branch from f44094f to 38391eb Compare July 17, 2026 01:20
@xyzconstant xyzconstant changed the title test: add coverage for immediate client disconnects in ListenConnections Keep listening after a failed accept() in ListenConnections Jul 17, 2026
@xyzconstant
xyzconstant marked this pull request as ready for review July 17, 2026 02:32

@ViniciusCestarii ViniciusCestarii left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ACK 38391eb 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.

Comment thread include/mp/proxy-io.h Outdated
@xyzconstant
xyzconstant force-pushed the server-disconnect-tests branch from 38391eb to 92d8f86 Compare July 17, 2026 15:53
@xyzconstant

Copy link
Copy Markdown
Contributor Author

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.

@xyzconstant
xyzconstant force-pushed the server-disconnect-tests branch from 92d8f86 to d8e5395 Compare July 17, 2026 16:05

@ryanofsky ryanofsky left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Code review 38391eb

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.

Comment thread test/mp/test/listen_tests.cpp Outdated
Comment thread test/mp/test/listen_tests.cpp Outdated
@ViniciusCestarii

Copy link
Copy Markdown
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

Add `DefaultLogHandler` and a `log_handler` parameter so tests can provide
their own handler and observe log output more easily.
@xyzconstant
xyzconstant force-pushed the server-disconnect-tests branch from d8e5395 to 5fc3064 Compare July 20, 2026 18:04
@xyzconstant xyzconstant changed the title Keep listening after a failed accept() in ListenConnections test: cover immediate client disconnects for ListenConnections Jul 20, 2026
@xyzconstant
xyzconstant force-pushed the server-disconnect-tests branch from 5fc3064 to b5b357f Compare July 20, 2026 18:31
@xyzconstant

Copy link
Copy Markdown
Contributor Author

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

re: #310 (review)

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.

@xyzconstant

Copy link
Copy Markdown
Contributor Author

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

@ryanofsky ryanofsky left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Code review ACK b5b357f. Thanks for the test! Left suggestions below but think this could also be merged as is.

Comment thread test/mp/test/listen_tests.cpp Outdated
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.
@xyzconstant
xyzconstant force-pushed the server-disconnect-tests branch from b5b357f to 496fb84 Compare July 30, 2026 14:52
@xyzconstant

Copy link
Copy Markdown
Contributor Author

Thanks for the review @ryanofsky!

I force-pushed changes based on your suggestion to tighten the KJ_EXPECT(...) check, plus the KJ_REQUIRE() check on accept_error_seen() for non-macOS builds.

b5b357f -> 496fb84 (compare)

@ryanofsky ryanofsky left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Code review ACK 496fb84. Since last review just tightened the checks to only allow the error on macos

// The event loop then reports this as an uncaught task exception. We catch and ignore
// this specific error here so that the corresponding CI job does not fail.
//
// This is a Cap'n Proto bug, a fix is available in the v2 branch at: https://github.com/capnproto/capnproto/commit/7df5bd078f389ded313479981bd0ae06cbcdfe1b#diff-ec577ad66535f58f6d7396ea51d3e56c0065308aa8fb02751cd6a8cfaa67252fR1358-R1372

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

In commit "test: cover immediate client disconnects for ListenConnections" (496fb84)

Now that the fix is merged would be nice to simplify the comment to something like This a Cap'n Proto bug, fixed by https://github.com/capnproto/capnproto/pull/2748

@ryanofsky
ryanofsky merged commit c437d7f into bitcoin-core:master Aug 3, 2026
13 checks passed
@ryanofsky ryanofsky mentioned this pull request Aug 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants