From 7109f59fffa41858a9ac72d2c338e0324587e45e Mon Sep 17 00:00:00 2001 From: Fabian Fett Date: Fri, 14 Jun 2024 01:01:12 +0200 Subject: [PATCH 1/3] More Swift 6 Sendable annotations --- Package.swift | 17 ++++++++++++++++- .../ConnectionPoolModule/ConnectionPool.swift | 4 ++-- .../ConnectionPoolObservabilityDelegate.swift | 2 +- .../Message/PostgresMessage+Identifier.swift | 2 +- Sources/PostgresNIO/Pool/PostgresClient.swift | 2 +- .../Utilities/PostgresError+Code.swift | 2 +- .../Mocks/MockConnectionFactory.swift | 2 +- .../New/PostgresConnectionTests.swift | 19 +++++++++++-------- 8 files changed, 34 insertions(+), 16 deletions(-) diff --git a/Package.swift b/Package.swift index d24ee979..3a3e3b48 100644 --- a/Package.swift +++ b/Package.swift @@ -41,6 +41,9 @@ let package = Package( .product(name: "NIOSSL", package: "swift-nio-ssl"), .product(name: "NIOFoundationCompat", package: "swift-nio"), .product(name: "ServiceLifecycle", package: "swift-service-lifecycle"), + ], + swiftSettings: [ + .enableUpcomingFeature("StrictConcurrency") ] ), .target( @@ -49,7 +52,10 @@ let package = Package( .product(name: "Atomics", package: "swift-atomics"), .product(name: "DequeModule", package: "swift-collections"), ], - path: "Sources/ConnectionPoolModule" + path: "Sources/ConnectionPoolModule", + swiftSettings: [ + .enableUpcomingFeature("StrictConcurrency") + ] ), .testTarget( name: "PostgresNIOTests", @@ -57,6 +63,9 @@ let package = Package( .target(name: "PostgresNIO"), .product(name: "NIOEmbedded", package: "swift-nio"), .product(name: "NIOTestUtils", package: "swift-nio"), + ], + swiftSettings: [ + .enableUpcomingFeature("StrictConcurrency") ] ), .testTarget( @@ -67,6 +76,9 @@ let package = Package( .product(name: "NIOCore", package: "swift-nio"), .product(name: "NIOConcurrencyHelpers", package: "swift-nio"), .product(name: "NIOEmbedded", package: "swift-nio"), + ], + swiftSettings: [ + .enableUpcomingFeature("StrictConcurrency") ] ), .testTarget( @@ -74,6 +86,9 @@ let package = Package( dependencies: [ .target(name: "PostgresNIO"), .product(name: "NIOTestUtils", package: "swift-nio"), + ], + swiftSettings: [ + .enableUpcomingFeature("StrictConcurrency") ] ), ] diff --git a/Sources/ConnectionPoolModule/ConnectionPool.swift b/Sources/ConnectionPoolModule/ConnectionPool.swift index 3231cc06..03c269ee 100644 --- a/Sources/ConnectionPoolModule/ConnectionPool.swift +++ b/Sources/ConnectionPoolModule/ConnectionPool.swift @@ -1,6 +1,6 @@ @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *) -public struct ConnectionAndMetadata { +public struct ConnectionAndMetadata: Sendable { public var connection: Connection @@ -495,7 +495,7 @@ public final class ConnectionPool< } @usableFromInline - enum TimerRunResult { + enum TimerRunResult: Sendable { case timerTriggered case timerCancelled case cancellationContinuationFinished diff --git a/Sources/ConnectionPoolModule/ConnectionPoolObservabilityDelegate.swift b/Sources/ConnectionPoolModule/ConnectionPoolObservabilityDelegate.swift index 35f30dcb..fc1e300c 100644 --- a/Sources/ConnectionPoolModule/ConnectionPoolObservabilityDelegate.swift +++ b/Sources/ConnectionPoolModule/ConnectionPoolObservabilityDelegate.swift @@ -37,7 +37,7 @@ public protocol ConnectionPoolObservabilityDelegate: Sendable { func requestQueueDepthChanged(_ newDepth: Int) } -public struct NoOpConnectionPoolMetrics: ConnectionPoolObservabilityDelegate { +public struct NoOpConnectionPoolMetrics: ConnectionPoolObservabilityDelegate { public init(connectionIDType: ConnectionID.Type) {} public func startedConnecting(id: ConnectionID) {} diff --git a/Sources/PostgresNIO/Message/PostgresMessage+Identifier.swift b/Sources/PostgresNIO/Message/PostgresMessage+Identifier.swift index 786b91ef..5d111e3b 100644 --- a/Sources/PostgresNIO/Message/PostgresMessage+Identifier.swift +++ b/Sources/PostgresNIO/Message/PostgresMessage+Identifier.swift @@ -4,7 +4,7 @@ extension PostgresMessage { /// Identifies an incoming or outgoing postgres message. Sent as the first byte, before the message size. /// Values are not unique across all identifiers, meaning some messages will require keeping state to identify. @available(*, deprecated, message: "Will be removed from public API.") - public struct Identifier: ExpressibleByIntegerLiteral, Equatable, CustomStringConvertible { + public struct Identifier: Sendable, ExpressibleByIntegerLiteral, Equatable, CustomStringConvertible { // special public static let none: Identifier = 0x00 // special diff --git a/Sources/PostgresNIO/Pool/PostgresClient.swift b/Sources/PostgresNIO/Pool/PostgresClient.swift index 2e1b7e11..0907f1f8 100644 --- a/Sources/PostgresNIO/Pool/PostgresClient.swift +++ b/Sources/PostgresNIO/Pool/PostgresClient.swift @@ -478,7 +478,7 @@ extension PostgresConnection: PooledConnection { self.channel.close(mode: .all, promise: nil) } - public func onClose(_ closure: @escaping ((any Error)?) -> ()) { + public func onClose(_ closure: @escaping @Sendable ((any Error)?) -> ()) { self.closeFuture.whenComplete { _ in closure(nil) } } } diff --git a/Sources/PostgresNIO/Utilities/PostgresError+Code.swift b/Sources/PostgresNIO/Utilities/PostgresError+Code.swift index 11224f4b..fae903fe 100644 --- a/Sources/PostgresNIO/Utilities/PostgresError+Code.swift +++ b/Sources/PostgresNIO/Utilities/PostgresError+Code.swift @@ -1,5 +1,5 @@ extension PostgresError { - public struct Code: ExpressibleByStringLiteral, Equatable { + public struct Code: Sendable, ExpressibleByStringLiteral, Equatable { // Class 00 — Successful Completion public static let successfulCompletion: Code = "00000" diff --git a/Tests/ConnectionPoolModuleTests/Mocks/MockConnectionFactory.swift b/Tests/ConnectionPoolModuleTests/Mocks/MockConnectionFactory.swift index eec2e7c3..1c9bfff8 100644 --- a/Tests/ConnectionPoolModuleTests/Mocks/MockConnectionFactory.swift +++ b/Tests/ConnectionPoolModuleTests/Mocks/MockConnectionFactory.swift @@ -2,7 +2,7 @@ import DequeModule @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *) -final class MockConnectionFactory where Clock.Duration == Duration { +final class MockConnectionFactory: Sendable where Clock.Duration == Duration { typealias ConnectionIDGenerator = _ConnectionPoolModule.ConnectionIDGenerator typealias Request = ConnectionRequest typealias KeepAliveBehavior = MockPingPongBehavior diff --git a/Tests/PostgresNIOTests/New/PostgresConnectionTests.swift b/Tests/PostgresNIOTests/New/PostgresConnectionTests.swift index 209522dd..0bc61efd 100644 --- a/Tests/PostgresNIOTests/New/PostgresConnectionTests.swift +++ b/Tests/PostgresNIOTests/New/PostgresConnectionTests.swift @@ -187,7 +187,7 @@ class PostgresConnectionTests: XCTestCase { func testSimpleListenConnectionDrops() async throws { let (connection, channel) = try await self.makeTestConnectionWithAsyncTestingChannel() - try await withThrowingTaskGroup(of: Void.self) { taskGroup in + try await withThrowingTaskGroup(of: Void.self) { [logger] taskGroup in taskGroup.addTask { let events = try await connection.listen("foo") var iterator = events.makeAsyncIterator() @@ -197,7 +197,7 @@ class PostgresConnectionTests: XCTestCase { _ = try await iterator.next() XCTFail("Did not expect to not throw") } catch { - self.logger.error("error", metadata: ["error": "\(error)"]) + logger.error("error", metadata: ["error": "\(error)"]) } } @@ -226,10 +226,10 @@ class PostgresConnectionTests: XCTestCase { func testCloseGracefullyClosesWhenInternalQueueIsEmpty() async throws { let (connection, channel) = try await self.makeTestConnectionWithAsyncTestingChannel() - try await withThrowingTaskGroup(of: Void.self) { taskGroup async throws -> () in + try await withThrowingTaskGroup(of: Void.self) { [logger] taskGroup async throws -> () in for _ in 1...2 { taskGroup.addTask { - let rows = try await connection.query("SELECT 1;", logger: self.logger) + let rows = try await connection.query("SELECT 1;", logger: logger) var iterator = rows.decode(Int.self).makeAsyncIterator() let first = try await iterator.next() XCTAssertEqual(first, 1) @@ -286,10 +286,10 @@ class PostgresConnectionTests: XCTestCase { func testCloseClosesImmediatly() async throws { let (connection, channel) = try await self.makeTestConnectionWithAsyncTestingChannel() - try await withThrowingTaskGroup(of: Void.self) { taskGroup async throws -> () in + try await withThrowingTaskGroup(of: Void.self) { [logger] taskGroup async throws -> () in for _ in 1...2 { taskGroup.addTask { - try await connection.query("SELECT 1;", logger: self.logger) + try await connection.query("SELECT 1;", logger: logger) } } @@ -319,8 +319,9 @@ class PostgresConnectionTests: XCTestCase { func testIfServerJustClosesTheErrorReflectsThat() async throws { let (connection, channel) = try await self.makeTestConnectionWithAsyncTestingChannel() + let logger = self.logger - async let response = try await connection.query("SELECT 1;", logger: self.logger) + async let response = try await connection.query("SELECT 1;", logger: logger) let listenMessage = try await channel.waitForUnpreparedRequest() XCTAssertEqual(listenMessage.parse.query, "SELECT 1;") @@ -423,6 +424,7 @@ class PostgresConnectionTests: XCTestCase { case pleaseDontCrash } channel.pipeline.fireUserInboundEventTriggered(MyEvent.pleaseDontCrash) + try await connection.close() } func testSerialExecutionOfSamePreparedStatement() async throws { @@ -651,7 +653,8 @@ class PostgresConnectionTests: XCTestCase { database: "database" ) - async let connectionPromise = PostgresConnection.connect(on: eventLoop, configuration: configuration, id: 1, logger: self.logger) + let logger = self.logger + async let connectionPromise = PostgresConnection.connect(on: eventLoop, configuration: configuration, id: 1, logger: logger) let message = try await channel.waitForOutboundWrite(as: PostgresFrontendMessage.self) XCTAssertEqual(message, .startup(.versionThree(parameters: .init(user: "username", database: "database", options: [], replication: .false)))) try await channel.writeInbound(PostgresBackendMessage.authentication(.ok)) From 8575bc70b2b40402e4fe5b858406e9ce95f3276b Mon Sep 17 00:00:00 2001 From: Fabian Fett Date: Fri, 14 Jun 2024 10:24:24 +0200 Subject: [PATCH 2/3] Add explicit Package.swift at Swift 6. --- Package.swift | 19 +-------- Package@swift-5.8.swift | 95 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 17 deletions(-) create mode 100644 Package@swift-5.8.swift diff --git a/Package.swift b/Package.swift index 3a3e3b48..28b257bb 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.8 +// swift-tools-version:6.0 import PackageDescription let package = Package( @@ -41,9 +41,6 @@ let package = Package( .product(name: "NIOSSL", package: "swift-nio-ssl"), .product(name: "NIOFoundationCompat", package: "swift-nio"), .product(name: "ServiceLifecycle", package: "swift-service-lifecycle"), - ], - swiftSettings: [ - .enableUpcomingFeature("StrictConcurrency") ] ), .target( @@ -52,10 +49,7 @@ let package = Package( .product(name: "Atomics", package: "swift-atomics"), .product(name: "DequeModule", package: "swift-collections"), ], - path: "Sources/ConnectionPoolModule", - swiftSettings: [ - .enableUpcomingFeature("StrictConcurrency") - ] + path: "Sources/ConnectionPoolModule" ), .testTarget( name: "PostgresNIOTests", @@ -63,9 +57,6 @@ let package = Package( .target(name: "PostgresNIO"), .product(name: "NIOEmbedded", package: "swift-nio"), .product(name: "NIOTestUtils", package: "swift-nio"), - ], - swiftSettings: [ - .enableUpcomingFeature("StrictConcurrency") ] ), .testTarget( @@ -76,9 +67,6 @@ let package = Package( .product(name: "NIOCore", package: "swift-nio"), .product(name: "NIOConcurrencyHelpers", package: "swift-nio"), .product(name: "NIOEmbedded", package: "swift-nio"), - ], - swiftSettings: [ - .enableUpcomingFeature("StrictConcurrency") ] ), .testTarget( @@ -86,9 +74,6 @@ let package = Package( dependencies: [ .target(name: "PostgresNIO"), .product(name: "NIOTestUtils", package: "swift-nio"), - ], - swiftSettings: [ - .enableUpcomingFeature("StrictConcurrency") ] ), ] diff --git a/Package@swift-5.8.swift b/Package@swift-5.8.swift new file mode 100644 index 00000000..86554f4f --- /dev/null +++ b/Package@swift-5.8.swift @@ -0,0 +1,95 @@ +// swift-tools-version:5.8 +import PackageDescription + +let package = Package( + name: "postgres-nio", + platforms: [ + .macOS(.v10_15), + .iOS(.v13), + .watchOS(.v6), + .tvOS(.v13), + ], + products: [ + .library(name: "PostgresNIO", targets: ["PostgresNIO"]), + .library(name: "_ConnectionPoolModule", targets: ["_ConnectionPoolModule"]), + ], + dependencies: [ + .package(url: "https://github.com/apple/swift-atomics.git", from: "1.2.0"), + .package(url: "https://github.com/apple/swift-collections.git", from: "1.0.4"), + .package(url: "https://github.com/apple/swift-nio.git", from: "2.59.0"), + .package(url: "https://github.com/apple/swift-nio-transport-services.git", from: "1.19.0"), + .package(url: "https://github.com/apple/swift-nio-ssl.git", from: "2.25.0"), + .package(url: "https://github.com/apple/swift-crypto.git", "2.0.0" ..< "4.0.0"), + .package(url: "https://github.com/apple/swift-metrics.git", from: "2.4.1"), + .package(url: "https://github.com/apple/swift-log.git", from: "1.5.3"), + .package(url: "https://github.com/swift-server/swift-service-lifecycle.git", from: "2.4.1"), + ], + targets: [ + .target( + name: "PostgresNIO", + dependencies: [ + .target(name: "_ConnectionPoolModule"), + .product(name: "Atomics", package: "swift-atomics"), + .product(name: "Crypto", package: "swift-crypto"), + .product(name: "Logging", package: "swift-log"), + .product(name: "Metrics", package: "swift-metrics"), + .product(name: "NIO", package: "swift-nio"), + .product(name: "NIOCore", package: "swift-nio"), + .product(name: "NIOPosix", package: "swift-nio"), + .product(name: "NIOTransportServices", package: "swift-nio-transport-services"), + .product(name: "NIOTLS", package: "swift-nio"), + .product(name: "NIOSSL", package: "swift-nio-ssl"), + .product(name: "NIOFoundationCompat", package: "swift-nio"), + .product(name: "ServiceLifecycle", package: "swift-service-lifecycle"), + ], + swiftSettings: [ + .enableUpcomingFeature("StrictConcurrency") + ] + ), + .target( + name: "_ConnectionPoolModule", + dependencies: [ + .product(name: "Atomics", package: "swift-atomics"), + .product(name: "DequeModule", package: "swift-collections"), + ], + path: "Sources/ConnectionPoolModule", + swiftSettings: [ + .enableUpcomingFeature("StrictConcurrency") + ] + ), + .testTarget( + name: "PostgresNIOTests", + dependencies: [ + .target(name: "PostgresNIO"), + .product(name: "NIOEmbedded", package: "swift-nio"), + .product(name: "NIOTestUtils", package: "swift-nio"), + ], + swiftSettings: [ + .enableUpcomingFeature("StrictConcurrency") + ] + ), + .testTarget( + name: "ConnectionPoolModuleTests", + dependencies: [ + .target(name: "_ConnectionPoolModule"), + .product(name: "DequeModule", package: "swift-collections"), + .product(name: "NIOCore", package: "swift-nio"), + .product(name: "NIOConcurrencyHelpers", package: "swift-nio"), + .product(name: "NIOEmbedded", package: "swift-nio"), + ], + swiftSettings: [ + .enableUpcomingFeature("StrictConcurrency") + ] + ), + .testTarget( + name: "IntegrationTests", + dependencies: [ + .target(name: "PostgresNIO"), + .product(name: "NIOTestUtils", package: "swift-nio"), + ], + swiftSettings: [ + .enableUpcomingFeature("StrictConcurrency") + ] + ), + ] +) From 290e24494cf0ed7cd77a0c72e377451dd492fede Mon Sep 17 00:00:00 2001 From: Fabian Fett Date: Fri, 14 Jun 2024 12:39:56 +0200 Subject: [PATCH 3/3] Enable `StrictConcurrency` checking --- Package.swift | 21 ++-- Package@swift-5.8.swift | 95 ------------------- Tests/IntegrationTests/PostgresNIOTests.swift | 61 ++++++------ 3 files changed, 46 insertions(+), 131 deletions(-) delete mode 100644 Package@swift-5.8.swift diff --git a/Package.swift b/Package.swift index 28b257bb..0683dbe9 100644 --- a/Package.swift +++ b/Package.swift @@ -1,6 +1,10 @@ -// swift-tools-version:6.0 +// swift-tools-version:5.8 import PackageDescription +let swiftSettings: [SwiftSetting] = [ + .enableUpcomingFeature("StrictConcurrency") +] + let package = Package( name: "postgres-nio", platforms: [ @@ -41,7 +45,8 @@ let package = Package( .product(name: "NIOSSL", package: "swift-nio-ssl"), .product(name: "NIOFoundationCompat", package: "swift-nio"), .product(name: "ServiceLifecycle", package: "swift-service-lifecycle"), - ] + ], + swiftSettings: swiftSettings ), .target( name: "_ConnectionPoolModule", @@ -49,7 +54,8 @@ let package = Package( .product(name: "Atomics", package: "swift-atomics"), .product(name: "DequeModule", package: "swift-collections"), ], - path: "Sources/ConnectionPoolModule" + path: "Sources/ConnectionPoolModule", + swiftSettings: swiftSettings ), .testTarget( name: "PostgresNIOTests", @@ -57,7 +63,8 @@ let package = Package( .target(name: "PostgresNIO"), .product(name: "NIOEmbedded", package: "swift-nio"), .product(name: "NIOTestUtils", package: "swift-nio"), - ] + ], + swiftSettings: swiftSettings ), .testTarget( name: "ConnectionPoolModuleTests", @@ -67,14 +74,16 @@ let package = Package( .product(name: "NIOCore", package: "swift-nio"), .product(name: "NIOConcurrencyHelpers", package: "swift-nio"), .product(name: "NIOEmbedded", package: "swift-nio"), - ] + ], + swiftSettings: swiftSettings ), .testTarget( name: "IntegrationTests", dependencies: [ .target(name: "PostgresNIO"), .product(name: "NIOTestUtils", package: "swift-nio"), - ] + ], + swiftSettings: swiftSettings ), ] ) diff --git a/Package@swift-5.8.swift b/Package@swift-5.8.swift deleted file mode 100644 index 86554f4f..00000000 --- a/Package@swift-5.8.swift +++ /dev/null @@ -1,95 +0,0 @@ -// swift-tools-version:5.8 -import PackageDescription - -let package = Package( - name: "postgres-nio", - platforms: [ - .macOS(.v10_15), - .iOS(.v13), - .watchOS(.v6), - .tvOS(.v13), - ], - products: [ - .library(name: "PostgresNIO", targets: ["PostgresNIO"]), - .library(name: "_ConnectionPoolModule", targets: ["_ConnectionPoolModule"]), - ], - dependencies: [ - .package(url: "https://github.com/apple/swift-atomics.git", from: "1.2.0"), - .package(url: "https://github.com/apple/swift-collections.git", from: "1.0.4"), - .package(url: "https://github.com/apple/swift-nio.git", from: "2.59.0"), - .package(url: "https://github.com/apple/swift-nio-transport-services.git", from: "1.19.0"), - .package(url: "https://github.com/apple/swift-nio-ssl.git", from: "2.25.0"), - .package(url: "https://github.com/apple/swift-crypto.git", "2.0.0" ..< "4.0.0"), - .package(url: "https://github.com/apple/swift-metrics.git", from: "2.4.1"), - .package(url: "https://github.com/apple/swift-log.git", from: "1.5.3"), - .package(url: "https://github.com/swift-server/swift-service-lifecycle.git", from: "2.4.1"), - ], - targets: [ - .target( - name: "PostgresNIO", - dependencies: [ - .target(name: "_ConnectionPoolModule"), - .product(name: "Atomics", package: "swift-atomics"), - .product(name: "Crypto", package: "swift-crypto"), - .product(name: "Logging", package: "swift-log"), - .product(name: "Metrics", package: "swift-metrics"), - .product(name: "NIO", package: "swift-nio"), - .product(name: "NIOCore", package: "swift-nio"), - .product(name: "NIOPosix", package: "swift-nio"), - .product(name: "NIOTransportServices", package: "swift-nio-transport-services"), - .product(name: "NIOTLS", package: "swift-nio"), - .product(name: "NIOSSL", package: "swift-nio-ssl"), - .product(name: "NIOFoundationCompat", package: "swift-nio"), - .product(name: "ServiceLifecycle", package: "swift-service-lifecycle"), - ], - swiftSettings: [ - .enableUpcomingFeature("StrictConcurrency") - ] - ), - .target( - name: "_ConnectionPoolModule", - dependencies: [ - .product(name: "Atomics", package: "swift-atomics"), - .product(name: "DequeModule", package: "swift-collections"), - ], - path: "Sources/ConnectionPoolModule", - swiftSettings: [ - .enableUpcomingFeature("StrictConcurrency") - ] - ), - .testTarget( - name: "PostgresNIOTests", - dependencies: [ - .target(name: "PostgresNIO"), - .product(name: "NIOEmbedded", package: "swift-nio"), - .product(name: "NIOTestUtils", package: "swift-nio"), - ], - swiftSettings: [ - .enableUpcomingFeature("StrictConcurrency") - ] - ), - .testTarget( - name: "ConnectionPoolModuleTests", - dependencies: [ - .target(name: "_ConnectionPoolModule"), - .product(name: "DequeModule", package: "swift-collections"), - .product(name: "NIOCore", package: "swift-nio"), - .product(name: "NIOConcurrencyHelpers", package: "swift-nio"), - .product(name: "NIOEmbedded", package: "swift-nio"), - ], - swiftSettings: [ - .enableUpcomingFeature("StrictConcurrency") - ] - ), - .testTarget( - name: "IntegrationTests", - dependencies: [ - .target(name: "PostgresNIO"), - .product(name: "NIOTestUtils", package: "swift-nio"), - ], - swiftSettings: [ - .enableUpcomingFeature("StrictConcurrency") - ] - ), - ] -) diff --git a/Tests/IntegrationTests/PostgresNIOTests.swift b/Tests/IntegrationTests/PostgresNIOTests.swift index 4d06c13e..ff59209b 100644 --- a/Tests/IntegrationTests/PostgresNIOTests.swift +++ b/Tests/IntegrationTests/PostgresNIOTests.swift @@ -1,5 +1,6 @@ import Logging @testable import PostgresNIO +import Atomics import XCTest import NIOCore import NIOPosix @@ -112,59 +113,59 @@ final class PostgresNIOTests: XCTestCase { XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) defer { XCTAssertNoThrow( try conn?.close().wait() ) } - var receivedNotifications: [PostgresMessage.NotificationResponse] = [] + let receivedNotifications = ManagedAtomic(0) conn?.addListener(channel: "example") { context, notification in - receivedNotifications.append(notification) + receivedNotifications.wrappingIncrement(ordering: .relaxed) + XCTAssertEqual(notification.channel, "example") + XCTAssertEqual(notification.payload, "") } XCTAssertNoThrow(_ = try conn?.simpleQuery("LISTEN example").wait()) XCTAssertNoThrow(_ = try conn?.simpleQuery("NOTIFY example").wait()) // Notifications are asynchronous, so we should run at least one more query to make sure we'll have received the notification response by then XCTAssertNoThrow(_ = try conn?.simpleQuery("SELECT 1").wait()) - XCTAssertEqual(receivedNotifications.count, 1) - XCTAssertEqual(receivedNotifications.first?.channel, "example") - XCTAssertEqual(receivedNotifications.first?.payload, "") + XCTAssertEqual(receivedNotifications.load(ordering: .relaxed), 1) } func testNotificationsNonEmptyPayload() { var conn: PostgresConnection? XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) defer { XCTAssertNoThrow( try conn?.close().wait() ) } - var receivedNotifications: [PostgresMessage.NotificationResponse] = [] + let receivedNotifications = ManagedAtomic(0) conn?.addListener(channel: "example") { context, notification in - receivedNotifications.append(notification) + receivedNotifications.wrappingIncrement(ordering: .relaxed) + XCTAssertEqual(notification.channel, "example") + XCTAssertEqual(notification.payload, "Notification payload example") } XCTAssertNoThrow(_ = try conn?.simpleQuery("LISTEN example").wait()) XCTAssertNoThrow(_ = try conn?.simpleQuery("NOTIFY example, 'Notification payload example'").wait()) // Notifications are asynchronous, so we should run at least one more query to make sure we'll have received the notification response by then XCTAssertNoThrow(_ = try conn?.simpleQuery("SELECT 1").wait()) - XCTAssertEqual(receivedNotifications.count, 1) - XCTAssertEqual(receivedNotifications.first?.channel, "example") - XCTAssertEqual(receivedNotifications.first?.payload, "Notification payload example") + XCTAssertEqual(receivedNotifications.load(ordering: .relaxed), 1) } func testNotificationsRemoveHandlerWithinHandler() { var conn: PostgresConnection? XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) defer { XCTAssertNoThrow( try conn?.close().wait() ) } - var receivedNotifications = 0 + let receivedNotifications = ManagedAtomic(0) conn?.addListener(channel: "example") { context, notification in - receivedNotifications += 1 + receivedNotifications.wrappingIncrement(ordering: .relaxed) context.stop() } XCTAssertNoThrow(_ = try conn?.simpleQuery("LISTEN example").wait()) XCTAssertNoThrow(_ = try conn?.simpleQuery("NOTIFY example").wait()) XCTAssertNoThrow(_ = try conn?.simpleQuery("NOTIFY example").wait()) XCTAssertNoThrow(_ = try conn?.simpleQuery("SELECT 1").wait()) - XCTAssertEqual(receivedNotifications, 1) + XCTAssertEqual(receivedNotifications.load(ordering: .relaxed), 1) } func testNotificationsRemoveHandlerOutsideHandler() { var conn: PostgresConnection? XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) defer { XCTAssertNoThrow( try conn?.close().wait() ) } - var receivedNotifications = 0 + let receivedNotifications = ManagedAtomic(0) let context = conn?.addListener(channel: "example") { context, notification in - receivedNotifications += 1 + receivedNotifications.wrappingIncrement(ordering: .relaxed) } XCTAssertNotNil(context) XCTAssertNoThrow(_ = try conn?.simpleQuery("LISTEN example").wait()) @@ -173,47 +174,47 @@ final class PostgresNIOTests: XCTestCase { context?.stop() XCTAssertNoThrow(_ = try conn?.simpleQuery("NOTIFY example").wait()) XCTAssertNoThrow(_ = try conn?.simpleQuery("SELECT 1").wait()) - XCTAssertEqual(receivedNotifications, 1) + XCTAssertEqual(receivedNotifications.load(ordering: .relaxed), 1) } func testNotificationsMultipleRegisteredHandlers() { var conn: PostgresConnection? XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) defer { XCTAssertNoThrow( try conn?.close().wait() ) } - var receivedNotifications1 = 0 + let receivedNotifications1 = ManagedAtomic(0) conn?.addListener(channel: "example") { context, notification in - receivedNotifications1 += 1 + receivedNotifications1.wrappingIncrement(ordering: .relaxed) } - var receivedNotifications2 = 0 + let receivedNotifications2 = ManagedAtomic(0) conn?.addListener(channel: "example") { context, notification in - receivedNotifications2 += 1 + receivedNotifications2.wrappingIncrement(ordering: .relaxed) } XCTAssertNoThrow(_ = try conn?.simpleQuery("LISTEN example").wait()) XCTAssertNoThrow(_ = try conn?.simpleQuery("NOTIFY example").wait()) XCTAssertNoThrow(_ = try conn?.simpleQuery("SELECT 1").wait()) - XCTAssertEqual(receivedNotifications1, 1) - XCTAssertEqual(receivedNotifications2, 1) + XCTAssertEqual(receivedNotifications1.load(ordering: .relaxed), 1) + XCTAssertEqual(receivedNotifications2.load(ordering: .relaxed), 1) } func testNotificationsMultipleRegisteredHandlersRemoval() throws { var conn: PostgresConnection? XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) defer { XCTAssertNoThrow( try conn?.close().wait() ) } - var receivedNotifications1 = 0 + let receivedNotifications1 = ManagedAtomic(0) XCTAssertNotNil(conn?.addListener(channel: "example") { context, notification in - receivedNotifications1 += 1 + receivedNotifications1.wrappingIncrement(ordering: .relaxed) context.stop() }) - var receivedNotifications2 = 0 + let receivedNotifications2 = ManagedAtomic(0) XCTAssertNotNil(conn?.addListener(channel: "example") { context, notification in - receivedNotifications2 += 1 + receivedNotifications2.wrappingIncrement(ordering: .relaxed) }) XCTAssertNoThrow(_ = try conn?.simpleQuery("LISTEN example").wait()) XCTAssertNoThrow(_ = try conn?.simpleQuery("NOTIFY example").wait()) XCTAssertNoThrow(_ = try conn?.simpleQuery("NOTIFY example").wait()) XCTAssertNoThrow(_ = try conn?.simpleQuery("SELECT 1").wait()) - XCTAssertEqual(receivedNotifications1, 1) - XCTAssertEqual(receivedNotifications2, 2) + XCTAssertEqual(receivedNotifications1.load(ordering: .relaxed), 1) + XCTAssertEqual(receivedNotifications2.load(ordering: .relaxed), 2) } func testNotificationHandlerFiltersOnChannel() { @@ -1283,11 +1284,11 @@ final class PostgresNIOTests: XCTestCase { XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait()) defer { XCTAssertNoThrow( try conn?.close().wait() ) } var queries: [[PostgresRow]]? - XCTAssertNoThrow(queries = try conn?.prepare(query: "SELECT $1::text as foo;", handler: { query in + XCTAssertNoThrow(queries = try conn?.prepare(query: "SELECT $1::text as foo;", handler: { [eventLoop] query in let a = query.execute(["a"]) let b = query.execute(["b"]) let c = query.execute(["c"]) - return EventLoopFuture.whenAllSucceed([a, b, c], on: self.eventLoop) + return EventLoopFuture.whenAllSucceed([a, b, c], on: eventLoop) }).wait()) XCTAssertEqual(queries?.count, 3) var resultIterator = queries?.makeIterator()