Skip to content

Commit

Permalink
Add some more Sendable annotations to NIOCore (#2656)
Browse files Browse the repository at this point in the history
# Motivation

We have a few more warnings left under strict concurrency checking in `NIOCore`. This fixes the bulk of them that are not tied to `ChannelHandler` or `ChannelPipeline`.

# Modification

Add more `Sendable` and `@Sendable` annotations and make sure to use types that are `Sendable` annotated.
  • Loading branch information
FranzBusch committed Feb 19, 2024
1 parent 011b3df commit 0fbf6f1
Show file tree
Hide file tree
Showing 6 changed files with 21 additions and 22 deletions.
8 changes: 4 additions & 4 deletions Sources/NIOCore/ChannelOption.swift
Original file line number Diff line number Diff line change
Expand Up @@ -293,23 293,23 @@ extension ChannelOptions {
/// Provides `ChannelOption`s to be used with a `Channel`, `Bootstrap` or `ServerBootstrap`.
public struct ChannelOptions: Sendable {
#if !os(Windows)
public static let socket = { (level: SocketOptionLevel, name: SocketOptionName) -> Types.SocketOption in
public static let socket: @Sendable (SocketOptionLevel, SocketOptionName) -> ChannelOptions.Types.SocketOption = { (level: SocketOptionLevel, name: SocketOptionName) -> Types.SocketOption in
.init(level: NIOBSDSocket.OptionLevel(rawValue: CInt(level)), name: NIOBSDSocket.Option(rawValue: CInt(name)))
}
#endif

/// - seealso: `SocketOption`.
public static let socketOption = { (name: NIOBSDSocket.Option) -> Types.SocketOption in
public static let socketOption: @Sendable (NIOBSDSocket.Option) -> ChannelOptions.Types.SocketOption = { (name: NIOBSDSocket.Option) -> Types.SocketOption in
.init(level: .socket, name: name)
}

/// - seealso: `SocketOption`.
public static let ipOption = { (name: NIOBSDSocket.Option) -> Types.SocketOption in
public static let ipOption: @Sendable (NIOBSDSocket.Option) -> ChannelOptions.Types.SocketOption = { (name: NIOBSDSocket.Option) -> Types.SocketOption in
.init(level: .ip, name: name)
}

/// - seealso: `SocketOption`.
public static let tcpOption = { (name: NIOBSDSocket.Option) -> Types.SocketOption in
public static let tcpOption: @Sendable (NIOBSDSocket.Option) -> ChannelOptions.Types.SocketOption = { (name: NIOBSDSocket.Option) -> Types.SocketOption in
.init(level: .tcp, name: name)
}

Expand Down
2 changes: 1 addition & 1 deletion Sources/NIOCore/DeadChannel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 75,7 @@ private final class DeadChannelCore: ChannelCore {
/// A `ChannelPipeline` that is associated with a closed `Channel` must be careful to no longer use that original
/// channel as it only holds an unowned reference to the original `Channel`. `DeadChannel` serves as a replacement
/// that can be used when the original `Channel` might no longer be valid.
internal final class DeadChannel: Channel {
internal final class DeadChannel: Channel, @unchecked Sendable {
let eventLoop: EventLoop
let pipeline: ChannelPipeline

Expand Down
21 changes: 10 additions & 11 deletions Sources/NIOCore/EventLoop.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1166,20 1166,19 @@ extension EventLoopGroup {

private func _syncShutdownGracefully() throws {
self._preconditionSafeToSyncShutdown(file: #fileID, line: #line)
let errorStorageLock = NIOLock()
var errorStorage: Error? = nil
let continuation = DispatchWorkItem {}
self.shutdownGracefully { error in
if let error = error {
errorStorageLock.withLock {
errorStorage = error
let error = NIOLockedValueBox<Error?>(nil)
let semaphore = DispatchSemaphore(value: 0)
self.shutdownGracefully { shutdownError in
if let shutdownError = shutdownError {
error.withLockedValue {
$0 = shutdownError
}
}
continuation.perform()
semaphore.signal()
}
continuation.wait()
try errorStorageLock.withLock {
if let error = errorStorage {
semaphore.wait()
try error.withLockedValue { error in
if let error = error {
throw error
}
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/NIOCore/Interfaces.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 64,7 @@ private extension ifaddrs {

/// A representation of a single network interface on a system.
@available(*, deprecated, renamed: "NIONetworkDevice")
public final class NIONetworkInterface {
public final class NIONetworkInterface: Sendable {
// This is a class because in almost all cases this will carry
// four structs that are backed by classes, and so will incur 4
// refcount operations each time it is copied.
Expand Down
2 changes: 1 addition & 1 deletion Sources/NIOCore/UniversalBootstrapSupport.swift
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 190,7 @@ public struct NIOClientTCPBootstrap {
///
/// - parameters:
/// - handler: A closure that initializes the provided `Channel`.
public func channelInitializer(_ handler: @escaping (Channel) -> EventLoopFuture<Void>) -> NIOClientTCPBootstrap {
public func channelInitializer(_ handler: @escaping @Sendable (Channel) -> EventLoopFuture<Void>) -> NIOClientTCPBootstrap {
return NIOClientTCPBootstrap(self.underlyingBootstrap.channelInitializer(handler),
tlsEnabler: self.tlsEnablerTypeErased)
}
Expand Down
8 changes: 4 additions & 4 deletions Tests/NIOPosixTests/BootstrapTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -562,21 562,21 @@ class BootstrapTest: XCTestCase {
func testConvenienceOptionsAreEquivalentUniversalClient() throws {
func setAndGetOption<Option>(option: Option, _ applyOptions : (NIOClientTCPBootstrap) -> NIOClientTCPBootstrap) throws
-> Option.Value where Option : ChannelOption {
var optionRead : EventLoopFuture<Option.Value>?
let optionPromise = self.group.next().makePromise(of: Option.Value.self)
XCTAssertNoThrow(try withTCPServerChannel(group: self.group) { server in
var channel: Channel? = nil
XCTAssertNoThrow(channel = try applyOptions(NIOClientTCPBootstrap(
ClientBootstrap(group: self.group), tls: NIOInsecureNoTLS()))
.channelInitializer { channel in optionRead = channel.getOption(option)
.channelInitializer { channel in
channel.getOption(option).cascade(to: optionPromise)
return channel.eventLoop.makeSucceededFuture(())
}
.connect(to: server.localAddress!)
.wait())
XCTAssertNotNil(optionRead)
XCTAssertNotNil(channel)
XCTAssertNoThrow(try channel?.close().wait())
})
return try optionRead!.wait()
return try optionPromise.futureResult.wait()
}

func checkOptionEquivalence<Option>(longOption: Option, setValue: Option.Value,
Expand Down

0 comments on commit 0fbf6f1

Please sign in to comment.