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

Enable strict concurrency checking for NIOHTTP1 #3115

Merged
merged 8 commits into from
Feb 19, 2025

Conversation

rnro
Copy link
Contributor

@rnro rnro commented Feb 12, 2025

Motivation:

To ensure NIOHTTP1 concurrency safety.

Modifications:

  • Enable strict concurrency checking in the package manifest.
  • Mark several objects Sendable with @preconcurrency annotations where they are returned in futures which may execute in arbitrary concurrency domains.
    • NIOTypedHTTPClientProtocolUpgrader
    • NIOTypedHTTPClientUpgradeConfiguration
    • NIOUpgradableHTTPServerPipelineConfiguration
    • NIOUpgradableHTTPClientPipelineConfiguration
    • NIOTypedHTTPServerProtocolUpgrader
    • NIOTypedHTTPServerUpgradeConfiguration
  • Mark handlers as explicitly not sendable
    • NIOTypedHTTPClientUpgradeHandler
    • NIOTypedHTTPServerUpgradeHandler
  • Added new Sendable type aliases:
    • NIOHTTPClientUpgradeSendableConfiguration
    • NIOHTTPServerUpgradeSendableConfiguration

Result:

No more concurrency warnings. Builds will warn and CI will fail if regressions are introduced.

@rnro rnro force-pushed the strict_concurrency_niohttp1 branch from 9f4bfc7 to 71328d5 Compare February 12, 2025 10:13
Copy link
Contributor

@glbrntt glbrntt left a comment

Choose a reason for hiding this comment

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

LGTM, thanks Rick!

rnro added 2 commits February 14, 2025 15:01
Motivation:

To ensure NIOHTTP1 concurrency safety.

Modifications:

* Enable strict concurrency checking in the package manifest.
* Mark several objects `Sendable` with `@preconcurrency` annotations
  where they are returned in futures which may execute in arbitrary
concurrency domains.
  * `HTTPServerProtocolUpgrader`
  * `NIOHTTPClientProtocolUpgrader`
  * `NIOTypedHTTPClientProtocolUpgrader`
  * `NIOUpgradableHTTPServerPipelineConfiguration`
  * `NIOUpgradableHTTPClientPipelineConfiguration`
  * `NIOTypedHTTPClientUpgradeConfiguration`
* Mark handlers as explicitly not sendable
  * `NIOTypedHTTPClientUpgradeHandler`
  * `NIOTypedHTTPServerUpgradeHandler`

Result:

No more concurrency warnings. Builds will warn and CI will fail if regressions are introduced.
* Make use of `assumeIsolated` instead of `NIOLoopBound`s
* Do not add `Sendable` requirement to `NIOHTTPClientProtocolUpgrader`
* Do not add `Sendable` requirement to `HTTPServerProtocolUpgrader`
@rnro rnro force-pushed the strict_concurrency_niohttp1 branch from 58292a9 to 073e64e Compare February 14, 2025 15:01
@rnro rnro added the 🆕 semver/minor Adds new public API. label Feb 17, 2025
@rnro rnro requested a review from glbrntt February 17, 2025 11:07
return {
self.upgradeState = .upgrading

// assumeIsolated is safe here because this is only called from channelRead
Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't the justification here that removeHTTPHandlers returns us a future on the right EL? Should it be modified to return an isolated ELF? Otherwise the guarantee is based on documentation vs types.

Comment on lines 275 to 276
upgrader: any NIOTypedHTTPServerProtocolUpgrader<UpgradeResult>, responseHeaders: HTTPHeaders,
proto: String
Copy link
Contributor

Choose a reason for hiding this comment

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

Given the weird formatting can we do one param per line?

Suggested change
upgrader: any NIOTypedHTTPServerProtocolUpgrader<UpgradeResult>, responseHeaders: HTTPHeaders,
proto: String
upgrader: any NIOTypedHTTPServerProtocolUpgrader<UpgradeResult>,
responseHeaders: HTTPHeaders,
proto: String

Copy link
Contributor

Choose a reason for hiding this comment

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

This one got a thumbs up but no action 🤷‍♂️

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry, there must have been more than one instance or something - I definitely tweaked formatting in code that looked like this 4a5073e#diff-df1f0e00e4593ea99b5fed7e3a37f6415e9531d25dcb6994d6ba0f0a8ecf6cebR316

@rnro rnro requested a review from glbrntt February 17, 2025 16:11
@rnro rnro enabled auto-merge (squash) February 19, 2025 09:49
@rnro rnro disabled auto-merge February 19, 2025 11:35
/// - Returns: An `EventLoopFuture` that will contain a callback to invoke if upgrade is requested, or nil if upgrade has failed. Never returns a failed future.
/// - Returns: An isolated `EventLoopFuture` that will contain a callback to invoke if upgrade is requested,
/// or nil if upgrade has failed. Never returns a failed future.
/// The `NIOLoopBound` is safe because it's only called after the hop in firstRequestHeadReceived
Copy link
Contributor

Choose a reason for hiding this comment

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

What NIOLoopBound?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is left over from a previous implementation- removed.

@@ -18,7 +18,8 @@ import NIOCore

/// Configuration for an upgradable HTTP pipeline.
@available(macOS 13, iOS 16, tvOS 16, watchOS 9, *)
public struct NIOUpgradableHTTPServerPipelineConfiguration<UpgradeResult: Sendable> {
@preconcurrency
Copy link
Contributor

Choose a reason for hiding this comment

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

No need for a preconcurrency annotation here.

@@ -146,7 +147,8 @@ extension ChannelPipeline.SynchronousOperations {

/// Configuration for an upgradable HTTP pipeline.
@available(macOS 13, iOS 16, tvOS 16, watchOS 9, *)
public struct NIOUpgradableHTTPClientPipelineConfiguration<UpgradeResult: Sendable> {
@preconcurrency
Copy link
Contributor

Choose a reason for hiding this comment

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

No need for a preconcurrency annotation here.

@@ -42,7 +43,8 @@ public protocol NIOTypedHTTPClientProtocolUpgrader<UpgradeResult> {

/// The upgrade configuration for the ``NIOTypedHTTPClientUpgradeHandler``.
@available(macOS 13, iOS 16, tvOS 16, watchOS 9, *)
public struct NIOTypedHTTPClientUpgradeConfiguration<UpgradeResult: Sendable> {
@preconcurrency
Copy link
Contributor

Choose a reason for hiding this comment

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

No need for a preconcurrency annotation here.

@@ -47,7 +48,8 @@ public protocol NIOTypedHTTPServerProtocolUpgrader<UpgradeResult> {

/// The upgrade configuration for the ``NIOTypedHTTPServerUpgradeHandler``.
@available(macOS 13, iOS 16, tvOS 16, watchOS 9, *)
public struct NIOTypedHTTPServerUpgradeConfiguration<UpgradeResult: Sendable> {
@preconcurrency
Copy link
Contributor

Choose a reason for hiding this comment

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

No need for preconcurrency annotation here.

}.flatMap { () -> EventLoopFuture<UpgradeResult> in
upgrader.upgrade(channel: channel, upgradeRequest: requestHead)
}.nonisolated().hop(to: context.eventLoop)
.assumeIsolated()
Copy link
Contributor

Choose a reason for hiding this comment

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

nonisolated().hop(to:).assumeIsolated() shouldn't do anything meaningful here. We were already isolated.

Copy link
Contributor

@Lukasa Lukasa left a comment

Choose a reason for hiding this comment

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

Nice one. Merging over the API breakage checker as that's expected.

@Lukasa Lukasa merged commit 5f60cee into apple:main Feb 19, 2025
32 of 35 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🆕 semver/minor Adds new public API.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants