Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Synchronous connection failures must close channels. #329

Merged
merged 1 commit into from Apr 18, 2018

Conversation

Lukasa
Copy link
Contributor

@Lukasa Lukasa commented Apr 18, 2018

Motivation:

When a connect() call returns an error other than EINPROGRESS, we
currently leave the FD registered and don't close the channel. This is
wrong, we should take this as a signal to close the socket immediately,
rathern than letting the selector tell us this FD is dead: after all,
we know it's dead.

Modifications:

Call close0() in synchronous connect failures.

Result:

Faster failures!
Resolves #322.

@Lukasa Lukasa added the patch-version-bump-only For PRs that when merged will only cause a bump of the patch version, ie. 1.0.x -> 1.0.(x+1) label Apr 18, 2018
promise?.fail(error: error)
assert(self.isOpen)
// We're going to set the promise as the pending connect promise, and let close0 fail it for us.
self.pendingConnect = promise
Copy link
Member

Choose a reason for hiding this comment

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

@Lukasa this actually made me aware of another issue (which we may want to handle in another issue tho..). The pendingConnect is currently failed after we call fireChannelInactive which I think is not what should be done as the promise should be notified before the callbacks imho.

@@ -917,7 +917,10 @@ class BaseSocketChannel<T: BaseSocket>: SelectableChannel, ChannelCore {
becomeActive0(promise: promise)
}
} catch let error {
promise?.fail(error: error)
assert(self.isOpen)
Copy link
Member

Choose a reason for hiding this comment

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

in other places we opted to go for assert(self.lifecycleManager.isRegistered). Maybe you also want to assert(!self.lifecycleManager.isActive) because we should be registered but not active here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That assertion is unnecessary because the do block covers it. I suppose I could assert isRegistered && !isActive, if you prefer.

Copy link
Member

@weissi weissi left a comment

Choose a reason for hiding this comment

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

looks pretty good, two nits

@@ -83,6 +83,41 @@ class ChannelLifecycleHandler: ChannelInboundHandler {
}
}

fileprivate class VerifyConnectionFailureHandler: ChannelInboundHandler {
Copy link
Member

Choose a reason for hiding this comment

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

would you mind putting this class at the bottom of the file? I don't really like when I need to search for the beginning of the actual test class as that is where Xcode puts the 'run all tests in this class' button. I think our test files should all start with the actual test class and not the helpers.

@Lukasa Lukasa force-pushed the cb-sync-connect-failure branch 3 times, most recently from e3c5670 to 57e9460 Compare April 18, 2018 12:54
@Lukasa
Copy link
Contributor Author

Lukasa commented Apr 18, 2018

Ok @weissi, feedback addressed.

@@ -917,7 +916,10 @@ class BaseSocketChannel<T: BaseSocket>: SelectableChannel, ChannelCore {
becomeActive0(promise: promise)
}
} catch let error {
promise?.fail(error: error)
assert(self.lifecycleManager.isRegistered && !self.lifecycleManager.isActive)
Copy link
Member

Choose a reason for hiding this comment

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

sorry, tiny nit, would you mind putting them in different lines asserts? As if one fails, the assertion message won't tell you what went wrong. Otherwise you'd need to add a message printing both :(

Motivation:

When a connect() call returns an error other than EINPROGRESS, we
currently leave the FD registered and don't close the channel. This is
wrong, we should take this as a signal to close the socket immediately,
rathern than letting the selector tell us this FD is dead: after all,
we know it's dead.

Modifications:

Call `close0()` in synchronous connect failures.

Result:

Faster failures!
@Lukasa
Copy link
Contributor Author

Lukasa commented Apr 18, 2018

ACK, feedback addressed.

Copy link
Member

@weissi weissi left a comment

Choose a reason for hiding this comment

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

awesome, ta

@weissi weissi merged commit 707b413 into apple:master Apr 18, 2018
@Lukasa Lukasa deleted the cb-sync-connect-failure branch April 18, 2018 15:26
@Lukasa Lukasa added this to the 1.5.0 milestone Apr 19, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
patch-version-bump-only For PRs that when merged will only cause a bump of the patch version, ie. 1.0.x -> 1.0.(x+1)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Synchronous connect errors do not properly unregister their selector.
3 participants