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

channelActive needs to happen before channelRead #204

Merged
merged 1 commit into from
Mar 21, 2018

Conversation

weissi
Copy link
Member

@weissi weissi commented Mar 20, 2018

Many thanks for @vlm for the repro! Fixes #196

Motivation:

readIfNeeded was triggered straight after the channel was registered
but before channelActive was called which is wrong.

Modifications:

Moved the calls to readIfNeeded into becomeActive0 and removed them
everywhere else (except some error path).

Result:

handle ChannelHandler lifecycle more correctly.

@weissi weissi changed the title channelActive needs to happen before channelRead [WIP-TEST-MISSING] channelActive needs to happen before channelRead Mar 20, 2018
Copy link
Member

@normanmaurer normanmaurer left a comment

Choose a reason for hiding this comment

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

LGTM in general but needs a test

@weissi weissi force-pushed the jw-read-after-active-not-registered branch 2 times, most recently from 2b09023 to 4ca8ae4 Compare March 21, 2018 13:47
@weissi weissi changed the title [WIP-TEST-MISSING] channelActive needs to happen before channelRead channelActive needs to happen before channelRead Mar 21, 2018
}

func channelActive(ctx: ChannelHandlerContext) {
var buffer = ctx.channel.allocator.buffer(capacity: 100)
Copy link
Member

Choose a reason for hiding this comment

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

nit: just allocate 4 as this is big enough for "foo" ;)

Copy link
Member Author

Choose a reason for hiding this comment

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

but what if it's in UTF-264 encoding that I'll announce tomorrow? :p

fixing :)

// this will fail as the socket is already pre-connected ;)
sc.connect(to: bootstrap.localAddress!).map {
XCTFail("should've failed")
}.mapIfError { error in
Copy link
Member

Choose a reason for hiding this comment

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

the indention doesn't look right...

}
}

let elg = MultiThreadedEventLoopGroup(numThreads: 1)
Copy link
Member

Choose a reason for hiding this comment

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

also shut this down in a defer block

@weissi weissi force-pushed the jw-read-after-active-not-registered branch from 4ca8ae4 to c72bac4 Compare March 21, 2018 14:00

private let writeDonePromise: EventLoopPromise<()>

init(writeDonePromise: EventLoopPromise<()>) {
Copy link
Member

Choose a reason for hiding this comment

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

@weissi we usually use EventLoopPromise<Void> I think... Not sure what is the "more swifty way tho". Thoughts ?

Copy link
Member Author

@weissi weissi Mar 21, 2018

Choose a reason for hiding this comment

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

@normanmaurer for functions that have 'no return value' (sure you could return () but you don't have to) I tend to use -> Void but the promises we succeed with the value (). Therefore I mostly use EventLoopPromise<()> as there is a value 'sent'. Our codebase has both:

$ git grep 'EventLoopPromise<Void>' | wc -l
     220
$ git grep 'EventLoopPromise<()>' | wc -l
      57
$ git grep 'EventLoopFuture<()>' | wc -l
      31
$ git grep 'EventLoopFuture<Void>' | wc -l
      69

Copy link
Member

Choose a reason for hiding this comment

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

@weissi maybe a follow-up but I think w should make it consistent ;) I am not sure what is the "preferred" way tho.

Copy link
Member Author

Choose a reason for hiding this comment

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

@normanmaurer ok, I'll file an issue.

Copy link
Member Author

Choose a reason for hiding this comment

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

see #211

defer {
XCTAssertNoThrow(try elg.syncShutdownGracefully())
}
let sc = try SocketChannel(eventLoop: elg.next() as! SelectableEventLoop, protocolFamily: AF_INET)
Copy link
Member

Choose a reason for hiding this comment

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

IMHO we use PF_INET usually, so maybe change this as well ?

Copy link
Contributor

Choose a reason for hiding this comment

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


let serverWriteHappenedPromise: EventLoopPromise<()> = elg.next().newPromise()
let clientHasRegistered: EventLoopPromise<()> = elg.next().newPromise()
let clientHasUnregistered: EventLoopPromise<()> = elg.next().newPromise()
Copy link
Member

Choose a reason for hiding this comment

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

same comment about using Void here.

Copy link
Member Author

Choose a reason for hiding this comment

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

let clientHasRegistered: EventLoopPromise<()> = elg.next().newPromise()
let clientHasUnregistered: EventLoopPromise<()> = elg.next().newPromise()

let bootstrap = try! ServerBootstrap(group: elg)
Copy link
Member

Choose a reason for hiding this comment

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

I think you can remove ! here.

Copy link
Member

@normanmaurer normanmaurer left a comment

Choose a reason for hiding this comment

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

A few more comments

@weissi weissi force-pushed the jw-read-after-active-not-registered branch 2 times, most recently from 4c2ff85 to 0d001f6 Compare March 21, 2018 14:06
}
}.wait()
try clientHasRegistered.futureResult.wait()
try sc.close().wait()
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this is very slightly probabilistic, as the close can technically race with the return to the selector such that it occurs before the selector is next polled. I think the only way you can avoid this is to connect-and-read-from another channel, which kinda sucks, but may be necessary.

@weissi weissi force-pushed the jw-read-after-active-not-registered branch 3 times, most recently from 3339426 to 3993ca3 Compare March 21, 2018 15:43
}
let serverEL = group.next()
let clientEL = group.next()
precondition(serverEL !== clientEL)
Copy link
Member

Choose a reason for hiding this comment

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

@weissi just make this an XCTAssertTrue ?

Copy link
Member Author

Choose a reason for hiding this comment

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

the problem is that if I do this, the test would just hang forever if something goes wrong (as XCTAsserts trigger after the test has finished)

Copy link
Member

Choose a reason for hiding this comment

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

I see... ok

sc.register()
}.then {
// this should fail (and will on Darwin but _not_ Linux) as the socket is already pre-connected ;)
// on Linux a subsequent connect will just silently succeed, go figure...
Copy link
Member

Choose a reason for hiding this comment

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

This really makes me sad :( I wonder if there is no other way to test this in a more "consistent" way

Copy link
Member Author

Choose a reason for hiding this comment

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

@normanmaurer after @Lukasa 's patch is in I can create a Socket subclass which lets a subsequent 'special' connect succeed on all platforms. That way we can test even more as the channel does become active.

Copy link
Member

Choose a reason for hiding this comment

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

@weissi which patch you are talking about ? :)

Copy link
Member Author

Choose a reason for hiding this comment

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

@normanmaurer I must have dreamt ;)

let serverEL = group.next()
let clientEL = group.next()
precondition(serverEL !== clientEL)
let sc = try SocketChannel(eventLoop: clientEL as! SelectableEventLoop, protocolFamily: PF_INET)
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd prefer AF_INET. 2c.

Copy link
Member Author

Choose a reason for hiding this comment

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

isn't AF address family and PF protocol family?

Copy link
Contributor

Choose a reason for hiding this comment

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

They're the same. There's no meaningful distinction here: the idea that these constants are different is a legacy from a bygone era.

Copy link
Member Author

Choose a reason for hiding this comment

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

@Lukasa so should I use PF_INET | AF_INET :P

Copy link
Member Author

Choose a reason for hiding this comment

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

@normanmaurer wanted PF_INET #204 (comment) . I don't care :)

Copy link
Member

Choose a reason for hiding this comment

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

@weissi @Lukasa to be clear I dont care which we use as long as we use it in a consistent way ;) I only suggested PF_INET because we use it in SocketAddress.

@weissi weissi force-pushed the jw-read-after-active-not-registered branch from 3993ca3 to 577dc76 Compare March 21, 2018 16:17
// available to be read immediately
sc.register()
}.then {
// this should fail (and will on Darwin but _not_ Linux) as the socket is already pre-connected ;)
Copy link
Contributor

Choose a reason for hiding this comment

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

This comment is out of date.

Copy link
Member Author

Choose a reason for hiding this comment

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

thanks, fixed

Motivation:

`readIfNeeded` was triggered straight after the channel was registered
but _before_ `channelActive` was called which is wrong.

Modifications:

Moved the calls to `readIfNeeded` into `becomeActive0` and removed them
everywhere else (except some error path).

Result:

handle ChannelHandler lifecycle more correctly.
@weissi weissi force-pushed the jw-read-after-active-not-registered branch from 577dc76 to a495ac0 Compare March 21, 2018 16:22
Copy link
Member

@normanmaurer normanmaurer left a comment

Choose a reason for hiding this comment

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

LGTM if it passes the CI :)

@Lukasa Lukasa merged commit 24fbb1b into apple:master Mar 21, 2018
@normanmaurer normanmaurer added this to the 1.3.0 milestone Mar 21, 2018
@normanmaurer normanmaurer added the 🔨 semver/patch No public API change. label Mar 21, 2018
weissi pushed a commit to weissi/swift-nio that referenced this pull request Jun 13, 2020
Motivation:

As Chrome has started GREASEing HTTP/2 settings and frames, we should
ensure that we have tests that explicitly validate that we tolerate
GREASEing these parameters.

A future version of swift-nio-http2 should start emitting GREASE as
well, but for now we'll satisfy ourselves with this.

Two relevant citations:

https://tools.ietf.org/html/draft-bishop-httpbis-grease-00

https://bugs.chromium.org/p/chromium/issues/detail?id=1083706

Modifications:

- Added a frame parser test
- Added a end-to-end settings test.

Result:

Validated that GREASE is tolerated.
@weissi weissi deleted the jw-read-after-active-not-registered branch July 9, 2024 14:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🔨 semver/patch No public API change.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants