Skip to content

Conversation

@gjcairo
Copy link
Contributor

@gjcairo gjcairo commented Mar 27, 2025

No description provided.

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.

Thanks Gus, this mostly looks great.

Package.swift Outdated
@@ -1,4 +1,4 @@
// swift-tools-version:5.8
// swift-tools-version:5.9
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you drop 5.8 in a separate PR please?

Package.swift Outdated

import PackageDescription

let strictConcurrencyDevelopment = true
Copy link
Contributor

Choose a reason for hiding this comment

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

This should be false!

let childChannelOptions = self.childChannelOptions

@inline(__always)
@preconcurrency @Sendable @inline(__always)
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we need @preconcurrency 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.

We don't, not sure why I added this

Comment on lines 60 to 61
private let readPromise: NIOLockedValueBox<EventLoopPromise<Void>?>
private let cumulationBuffer: NIOLockedValueBox<ByteBuffer?>
Copy link
Contributor

Choose a reason for hiding this comment

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

Not a big deal but I would bias away from using multiple locks (easy to run into ordering issues where you deadlock). In this instance I don't think I'd even make the class Sendable, I'd just pass the optional promise to the init. cumulationBuffer is isolated to the event-loop anyway.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It needs to be Sendable because we use it in Channel/expectRead(_:) (defined in this file too) and we're not using an embedded EL so I don't think we can assume isolated anywhere.

Copy link
Contributor

Choose a reason for hiding this comment

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

You don't need to use assume isolated here; you can create the promise off of the event-loop then create the handler and init it with the promise on the right event-loop. I.e. only the promise needs to be sendable here.

@gjcairo gjcairo added the 🔨 semver/patch No public API change. label Mar 27, 2025
@gjcairo gjcairo requested a review from glbrntt March 27, 2025 23:58

#if canImport(Network)
import Dispatch
@preconcurrency import Dispatch
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm a bit suspicious of this, why do we need it? I thought Dispatch had adopted Sendable etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The macOS CI (that I added temporarily in a commit to check it all built properly) failed without @preconcurrency.

Copy link
Contributor

Choose a reason for hiding this comment

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

Any idea why/where it failed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

/Users/github/actions-runner/_work/swift-nio-transport-services/swift-nio-transport-services/Sources/NIOTransportServices/NIOTSEventLoop.swift:207:13: error: capture of 'timerSource' with non-sendable type 'any DispatchSourceTimer' in a `@Sendable` closure; this is an error in the Swift 6 language mode
            timerSource.cancel()
            ^
Dispatch.DispatchSourceTimer:1:17: note: protocol 'DispatchSourceTimer' does not conform to the 'Sendable' protocol
public protocol DispatchSourceTimer : DispatchSourceProtocol {
                ^
/Users/github/actions-runner/_work/swift-nio-transport-services/swift-nio-transport-services/Sources/NIOTransportServices/NIOTSEventLoop.swift:16:1: error: add '@preconcurrency' to suppress 'Sendable'-related warnings from module 'Dispatch'
import Dispatch
^
@preconcurrency 
/Users/github/actions-runner/_work/swift-nio-transport-services/swift-nio-transport-services/Sources/NIOTransportServices/NIOTSEventLoop.swift:213:17: error: capture of 'timerSource' with non-sendable type 'any DispatchSourceTimer' in a `@Sendable` closure; this is an error in the Swift 6 language mode
                timerSource.cancel()
                ^
Dispatch.DispatchSourceTimer:1:17: note: protocol 'DispatchSourceTimer' does not conform to the 'Sendable' protocol
public protocol DispatchSourceTimer : DispatchSourceProtocol {
                ^
Command SwiftCompile failed with a nonzero exit code

Xcode 16.0, 16.1, 16.2

Copy link
Contributor

Choose a reason for hiding this comment

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

DispatchSourceTimer is Sendable according to https://developer.apple.com/documentation/dispatch/dispatchsourcetimer

It's not clear when it was made Sendable but I think it's worth digging into this some more as I don't think we should import this with a catch-all @preconcurrency import.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Looks like DispatchSourceTimer conforms to Sendable only from Xcode 16.3 (i.e. Swift 6.1). I've added a compiler guard to include @preconcurrency only if the Swift version is <6.1

private var open: Bool {
self.state == .active
}
private let state = NIOLockedValueBox(LifecycleState.active(registeredChannels: [:]))
Copy link
Contributor

Choose a reason for hiding this comment

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

No for LifecycleState, yes for NIOTSEventLoop. IMO that's the right move though: the lock is completely superfluous here as the state is isolated to the event-loop. The lock just tells the compiler to shut up and adds overhead.

Comment on lines 60 to 61
private let readPromise: NIOLockedValueBox<EventLoopPromise<Void>?>
private let cumulationBuffer: NIOLockedValueBox<ByteBuffer?>
Copy link
Contributor

Choose a reason for hiding this comment

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

You don't need to use assume isolated here; you can create the promise off of the event-loop then create the handler and init it with the promise on the right event-loop. I.e. only the promise needs to be sendable here.

@gjcairo gjcairo requested a review from glbrntt April 1, 2025 13:00
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.

A couple of small things need fixing but LGTM otherwise!

Comment on lines +156 to +160
p.assumeIsolated().completeWith(
Result {
try task()
}
)
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't the scheduleTask func take a Sendable closure? (The other variants do.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah good catch - I think I also missed @preconcurrency on all these public methods where I've added @Sendable to the closures.

I think I'll still have to do the assumeIsolated dance though, because T isn't Sendable.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, good point.

@gjcairo gjcairo requested a review from glbrntt April 1, 2025 16:07
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 @gjcairo!

@gjcairo gjcairo merged commit 92bb536 into apple:main Apr 2, 2025
26 checks passed
@gjcairo gjcairo deleted the strict-concurrency branch April 2, 2025 09:54
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.

2 participants