Skip to content
26 changes: 23 additions & 3 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,31 @@

import PackageDescription

let strictConcurrencyDevelopment = false

let strictConcurrencySettings: [SwiftSetting] = {
var initialSettings: [SwiftSetting] = []
initialSettings.append(contentsOf: [
.enableUpcomingFeature("StrictConcurrency"),
.enableUpcomingFeature("InferSendableFromCaptures"),
])

if strictConcurrencyDevelopment {
// -warnings-as-errors here is a workaround so that IDE-based development can
// get tripped up on -require-explicit-sendable.
initialSettings.append(.unsafeFlags(["-require-explicit-sendable", "-warnings-as-errors"]))
}

return initialSettings
}()

let package = Package(
name: "swift-nio-transport-services",
products: [
.library(name: "NIOTransportServices", targets: ["NIOTransportServices"])
],
dependencies: [
.package(url: "https://github.com/apple/swift-nio.git", from: "2.62.0"),
.package(url: "https://github.com/apple/swift-nio.git", from: "2.81.0"),
.package(url: "https://github.com/apple/swift-atomics.git", from: "1.0.2"),
],
targets: [
Expand All @@ -33,7 +51,8 @@ let package = Package(
.product(name: "NIOFoundationCompat", package: "swift-nio"),
.product(name: "NIOTLS", package: "swift-nio"),
.product(name: "Atomics", package: "swift-atomics"),
]
],
swiftSettings: strictConcurrencySettings
),
.executableTarget(
name: "NIOTSHTTPClient",
Expand All @@ -58,7 +77,8 @@ let package = Package(
.product(name: "NIOCore", package: "swift-nio"),
.product(name: "NIOEmbedded", package: "swift-nio"),
.product(name: "Atomics", package: "swift-atomics"),
]
],
swiftSettings: strictConcurrencySettings
),
]
)
Expand Down
18 changes: 11 additions & 7 deletions Sources/NIOTransportServices/AcceptHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ internal class AcceptHandler<ChildChannel: Channel>: ChannelInboundHandler {
typealias InboundIn = ChildChannel
typealias InboundOut = ChildChannel

private let childChannelInitializer: ((Channel) -> EventLoopFuture<Void>)?
private let childChannelInitializer: (@Sendable (Channel) -> EventLoopFuture<Void>)?
private let childChannelOptions: ChannelOptions.Storage

init(
childChannelInitializer: ((Channel) -> EventLoopFuture<Void>)?,
childChannelInitializer: (@Sendable (Channel) -> EventLoopFuture<Void>)?,
childChannelOptions: ChannelOptions.Storage
) {
self.childChannelInitializer = childChannelInitializer
Expand All @@ -35,11 +35,12 @@ internal class AcceptHandler<ChildChannel: Channel>: ChannelInboundHandler {
let newChannel = self.unwrapInboundIn(data)
let childLoop = newChannel.eventLoop
let ctxEventLoop = context.eventLoop
let childInitializer = self.childChannelInitializer ?? { _ in childLoop.makeSucceededFuture(()) }
let childInitializer = self.childChannelInitializer ?? { @Sendable _ in childLoop.makeSucceededFuture(()) }
let childChannelOptions = self.childChannelOptions

@inline(__always)
@Sendable @inline(__always)
func setupChildChannel() -> EventLoopFuture<Void> {
self.childChannelOptions.applyAllChannelOptions(to: newChannel).flatMap { () -> EventLoopFuture<Void> in
childChannelOptions.applyAllChannelOptions(to: newChannel).flatMap { () -> EventLoopFuture<Void> in
childLoop.assertInEventLoop()
return childInitializer(newChannel)
}
Expand All @@ -48,8 +49,8 @@ internal class AcceptHandler<ChildChannel: Channel>: ChannelInboundHandler {
@inline(__always)
func fireThroughPipeline(_ future: EventLoopFuture<Void>) {
ctxEventLoop.assertInEventLoop()
future.flatMap { (_) -> EventLoopFuture<Void> in
ctxEventLoop.assertInEventLoop()
assert(ctxEventLoop === context.eventLoop)
future.assumeIsolated().flatMap { (_) -> EventLoopFuture<Void> in
guard context.channel.isActive else {
return newChannel.close().flatMapThrowing {
throw ChannelError.ioOnClosedChannel
Expand All @@ -75,4 +76,7 @@ internal class AcceptHandler<ChildChannel: Channel>: ChannelInboundHandler {
}
}
}

@available(*, unavailable)
extension AcceptHandler: Sendable {}
#endif
21 changes: 13 additions & 8 deletions Sources/NIOTransportServices/Datagram/NIOTSDatagramBootstrap.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ import Network
@available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
public final class NIOTSDatagramBootstrap {
private let group: EventLoopGroup
private var channelInitializer: ((Channel) -> EventLoopFuture<Void>)?
private var channelInitializer: (@Sendable (Channel) -> EventLoopFuture<Void>)?
private var connectTimeout: TimeAmount = TimeAmount.seconds(10)
private var channelOptions = ChannelOptions.Storage()
private var qos: DispatchQoS?
Expand Down Expand Up @@ -79,7 +79,8 @@ public final class NIOTSDatagramBootstrap {
///
/// - parameters:
/// - handler: A closure that initializes the provided `Channel`.
public func channelInitializer(_ handler: @escaping (Channel) -> EventLoopFuture<Void>) -> Self {
@preconcurrency
public func channelInitializer(_ handler: @Sendable @escaping (Channel) -> EventLoopFuture<Void>) -> Self {
self.channelInitializer = handler
return self
}
Expand Down Expand Up @@ -180,17 +181,18 @@ public final class NIOTSDatagramBootstrap {
}
}

private func connect0(_ binder: @escaping (Channel, EventLoopPromise<Void>) -> Void) -> EventLoopFuture<Channel> {
private func connect0(
_ binder: @Sendable @escaping (Channel, EventLoopPromise<Void>) -> Void
) -> EventLoopFuture<Channel> {
let conn: Channel = NIOTSDatagramChannel(
eventLoop: self.group.next() as! NIOTSEventLoop,
qos: self.qos,
udpOptions: self.udpOptions,
tlsOptions: self.tlsOptions
)
let initializer = self.channelInitializer ?? { _ in conn.eventLoop.makeSucceededFuture(()) }
let channelOptions = self.channelOptions
let initializer = self.channelInitializer ?? { @Sendable _ in conn.eventLoop.makeSucceededFuture(()) }

return conn.eventLoop.submit {
return conn.eventLoop.submit { [channelOptions, connectTimeout] in
channelOptions.applyAllChannelOptions(to: conn).flatMap {
initializer(conn)
}.flatMap {
Expand All @@ -199,8 +201,8 @@ public final class NIOTSDatagramBootstrap {
}.flatMap {
let connectPromise: EventLoopPromise<Void> = conn.eventLoop.makePromise()
binder(conn, connectPromise)
let cancelTask = conn.eventLoop.scheduleTask(in: self.connectTimeout) {
connectPromise.fail(ChannelError.connectTimeout(self.connectTimeout))
let cancelTask = conn.eventLoop.scheduleTask(in: connectTimeout) {
connectPromise.fail(ChannelError.connectTimeout(connectTimeout))
conn.close(promise: nil)
}

Expand All @@ -215,4 +217,7 @@ public final class NIOTSDatagramBootstrap {
}.flatMap { $0 }
}
}

@available(*, unavailable)
extension NIOTSDatagramBootstrap: Sendable {}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -243,4 +243,7 @@ extension NIOTSDatagramChannel {
SynchronousOptions(channel: self)
}
}

@available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
extension NIOTSDatagramChannel: @unchecked Sendable {}
#endif
37 changes: 24 additions & 13 deletions Sources/NIOTransportServices/Datagram/NIOTSDatagramListener.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ import Network
public final class NIOTSDatagramListenerBootstrap {
private let group: EventLoopGroup
private let childGroup: EventLoopGroup
private var serverChannelInit: ((Channel) -> EventLoopFuture<Void>)?
private var childChannelInit: ((Channel) -> EventLoopFuture<Void>)?
private var serverChannelInit: (@Sendable (Channel) -> EventLoopFuture<Void>)?
private var childChannelInit: (@Sendable (Channel) -> EventLoopFuture<Void>)?
private var serverChannelOptions = ChannelOptions.Storage()
private var childChannelOptions = ChannelOptions.Storage()
private var serverQoS: DispatchQoS?
Expand Down Expand Up @@ -154,7 +154,9 @@ public final class NIOTSDatagramListenerBootstrap {
///
/// - parameters:
/// - initializer: A closure that initializes the provided `Channel`.
public func serverChannelInitializer(_ initializer: @escaping (Channel) -> EventLoopFuture<Void>) -> Self {
@preconcurrency
public func serverChannelInitializer(_ initializer: @Sendable @escaping (Channel) -> EventLoopFuture<Void>) -> Self
{
self.serverChannelInit = initializer
return self
}
Expand All @@ -167,7 +169,8 @@ public final class NIOTSDatagramListenerBootstrap {
///
/// - parameters:
/// - initializer: A closure that initializes the provided `Channel`.
public func childChannelInitializer(_ initializer: @escaping (Channel) -> EventLoopFuture<Void>) -> Self {
@preconcurrency
public func childChannelInitializer(_ initializer: @Sendable @escaping (Channel) -> EventLoopFuture<Void>) -> Self {
self.childChannelInit = initializer
return self
}
Expand Down Expand Up @@ -305,10 +308,13 @@ public final class NIOTSDatagramListenerBootstrap {
private func bind0(
existingNWListener: NWListener? = nil,
shouldRegister: Bool,
_ binder: @escaping (NIOTSDatagramListenerChannel, EventLoopPromise<Void>) -> Void
_ binder: @Sendable @escaping (NIOTSDatagramListenerChannel, EventLoopPromise<Void>) -> Void
) -> EventLoopFuture<Channel> {
let eventLoop = self.group.next() as! NIOTSEventLoop
let serverChannelInit = self.serverChannelInit ?? { _ in eventLoop.makeSucceededFuture(()) }
let serverChannelInit =
self.serverChannelInit ?? {
@Sendable _ in eventLoop.makeSucceededFuture(())
}
let childChannelInit = self.childChannelInit
let serverChannelOptions = self.serverChannelOptions
let childChannelOptions = self.childChannelOptions
Expand Down Expand Up @@ -339,17 +345,19 @@ public final class NIOTSDatagramListenerBootstrap {
)
}

return eventLoop.submit {
return eventLoop.submit { [bindTimeout] in
serverChannelOptions.applyAllChannelOptions(to: serverChannel).flatMap {
serverChannelInit(serverChannel)
}.flatMap {
eventLoop.assertInEventLoop()
return serverChannel.pipeline.addHandler(
AcceptHandler<NIOTSDatagramChannel>(
childChannelInitializer: childChannelInit,
childChannelOptions: childChannelOptions
return eventLoop.makeCompletedFuture {
try serverChannel.pipeline.syncOperations.addHandler(
AcceptHandler<NIOTSDatagramChannel>(
childChannelInitializer: childChannelInit,
childChannelOptions: childChannelOptions
)
)
)
}
}.flatMap {
if shouldRegister {
return serverChannel.register()
Expand All @@ -360,7 +368,7 @@ public final class NIOTSDatagramListenerBootstrap {
let bindPromise = eventLoop.makePromise(of: Void.self)
binder(serverChannel, bindPromise)

if let bindTimeout = self.bindTimeout {
if let bindTimeout = bindTimeout {
let cancelTask = eventLoop.scheduleTask(in: bindTimeout) {
bindPromise.fail(NIOTSErrors.BindTimeout(timeout: bindTimeout))
serverChannel.close(promise: nil)
Expand All @@ -382,4 +390,7 @@ public final class NIOTSDatagramListenerBootstrap {
}
}
}

@available(*, unavailable)
extension NIOTSDatagramListenerBootstrap: Sendable {}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ internal final class NIOTSDatagramListenerChannel: StateManagedListenerChannel<N
tlsOptions: self.childTLSOptions
)

self.pipeline.fireChannelRead(NIOAny(newChannel))
self.pipeline.fireChannelRead(newChannel)
self.pipeline.fireChannelReadComplete()
}

Expand Down
8 changes: 4 additions & 4 deletions Sources/NIOTransportServices/NIOTSChannelOptions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@
//===----------------------------------------------------------------------===//
#if canImport(Network)
import NIOCore
@preconcurrency import Network
import Network

/// Options that can be set explicitly and only on bootstraps provided by `NIOTransportServices`.
@available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
public struct NIOTSChannelOptions {
public struct NIOTSChannelOptions: Sendable {
/// See: ``Types/NIOTSWaitForActivityOption``.
public static let waitForActivity = NIOTSChannelOptions.Types.NIOTSWaitForActivityOption()

Expand All @@ -32,7 +32,7 @@ public struct NIOTSChannelOptions {

/// See: ``Types/NIOTSMetadataOption``
public static let metadata = {
(definition: NWProtocolDefinition) -> NIOTSChannelOptions.Types.NIOTSMetadataOption in
@Sendable (definition: NWProtocolDefinition) -> NIOTSChannelOptions.Types.NIOTSMetadataOption in
.init(definition: definition)
}

Expand Down Expand Up @@ -66,7 +66,7 @@ public struct NIOTSChannelOptions {
@available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
extension NIOTSChannelOptions {
/// A namespace for ``NIOTSChannelOptions`` datastructures.
public enum Types {
public enum Types: Sendable {
/// ``NIOTSWaitForActivityOption`` controls whether the `Channel` should wait for connection changes
/// during the connection process if the connection attempt fails. If Network.framework believes that
/// a connection may succeed in future, it may transition into the `.waiting` state. By default, this option
Expand Down
Loading