From 9bf86f97e1cc2a29d824859c72ceb2d8337c6b55 Mon Sep 17 00:00:00 2001 From: Fabian Fett Date: Thu, 13 Nov 2025 16:37:46 +0100 Subject: [PATCH] Enable NonIsolatedNonSendableByDefault In order to enable uses of `nonisolated(nonsending)` in #3378 without adding this sequence of characters everywhere, this pr enables the upcoming feature `NonIsolatedNonSendableByDefault ` --- Package.swift | 3 +- Sources/NIOCore/AsyncAwaitSupport.swift | 10 ++- .../AsyncChannelInboundStream.swift | 15 +++++ .../AsyncChannelOutboundWriter.swift | 6 ++ Sources/NIOCore/NIODecodedAsyncSequence.swift | 1 + Sources/NIOFS/DirectoryEntries.swift | 17 +++++ Sources/NIOFS/FileChunks.swift | 6 ++ .../NIOFS/Internal/BufferedOrAnyStream.swift | 21 +++++++ Sources/NIOPerformanceTester/main.swift | 2 +- .../Internal/BufferedOrAnyStream.swift | 8 ++- .../AsyncChannel/AsyncChannelTests.swift | 10 +-- .../NIOAsyncSequenceTests.swift | 1 + .../NIOThrowingAsyncSequenceTests.swift | 14 ++--- Tests/NIOCoreTests/XCTest+AsyncAwait.swift | 10 +-- .../NIOEmbeddedTests/XCTest+AsyncAwait.swift | 2 + .../FileHandleTests.swift | 5 +- .../FileSystemTests.swift | 10 +-- .../BufferedStreamTests.swift | 2 +- .../AsyncChannelBootstrapTests.swift | 62 +++++++++---------- .../NIOScheduledCallbackTests.swift | 4 +- Tests/NIOPosixTests/XCTest+AsyncAwait.swift | 2 +- 21 files changed, 150 insertions(+), 61 deletions(-) diff --git a/Package.swift b/Package.swift index a48a1b34274..6608a65cc27 100644 --- a/Package.swift +++ b/Package.swift @@ -28,7 +28,8 @@ let swiftSettings: [SwiftSetting] = [ // this "experimental" feature flag without two subsequent releases. We assume they will respect that // promise, so we enable this here. For more, see: // https://forums.swift.org/t/experimental-support-for-lifetime-dependencies-in-swift-6-2-and-beyond/78638 - .enableExperimentalFeature("Lifetimes") + .enableExperimentalFeature("Lifetimes"), + .enableUpcomingFeature("NonisolatedNonsendingByDefault"), ] // This doesn't work when cross-compiling: the privacy manifest will be included in the Bundle and diff --git a/Sources/NIOCore/AsyncAwaitSupport.swift b/Sources/NIOCore/AsyncAwaitSupport.swift index bd49ac1b67f..5072d4ed26f 100644 --- a/Sources/NIOCore/AsyncAwaitSupport.swift +++ b/Sources/NIOCore/AsyncAwaitSupport.swift @@ -425,7 +425,15 @@ extension AsyncSequence where Element == ByteBuffer { // this has also the benefit of not copying at all, // if the async sequence contains only one element. var iterator = self.makeAsyncIterator() - guard var head = try await iterator.next() else { + let head: ByteBuffer? + if #available(macOS 15.0, iOS 18.0, tvOS 18.0, watchOS 11.0, *) { + head = try await iterator.next(isolation: #isolation) + } else { + var box = UnsafeTransfer(iterator) + head = try await box.wrappedValue.next() + } + + guard var head = head else { return ByteBuffer() } guard head.readableBytes <= maxBytes else { diff --git a/Sources/NIOCore/AsyncChannel/AsyncChannelInboundStream.swift b/Sources/NIOCore/AsyncChannel/AsyncChannelInboundStream.swift index 31d89e7a227..1819e8e0792 100644 --- a/Sources/NIOCore/AsyncChannel/AsyncChannelInboundStream.swift +++ b/Sources/NIOCore/AsyncChannel/AsyncChannelInboundStream.swift @@ -134,6 +134,7 @@ extension NIOAsyncChannelInboundStream: AsyncSequence { } @inlinable + @concurrent public mutating func next() async throws -> Element? { switch self._backing { case .asyncStream(var iterator): @@ -147,6 +148,20 @@ extension NIOAsyncChannelInboundStream: AsyncSequence { return try await iterator.next() } } + + @available(macOS 15.0, iOS 18.0, tvOS 18.0, watchOS 11.0, *) + mutating public func next(isolation actor: isolated (any Actor)?) async throws(any Error) -> Inbound? { + switch self._backing { + case .asyncStream(var iterator): + defer { + self._backing = .asyncStream(iterator) + } + return try await iterator.next(isolation: actor) + + case .producer(let iterator): + return try await iterator.next() + } + } } @inlinable diff --git a/Sources/NIOCore/AsyncChannel/AsyncChannelOutboundWriter.swift b/Sources/NIOCore/AsyncChannel/AsyncChannelOutboundWriter.swift index 1ffd665ee22..d2604ff05f9 100644 --- a/Sources/NIOCore/AsyncChannel/AsyncChannelOutboundWriter.swift +++ b/Sources/NIOCore/AsyncChannel/AsyncChannelOutboundWriter.swift @@ -57,9 +57,15 @@ public struct NIOAsyncChannelOutboundWriter: Sendable { self.iterator = iterator } + @concurrent public mutating func next() async -> Element? { await self.iterator.next() } + + @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) + public mutating func next(isolation actor: isolated (any Actor)?) async -> Element? { + await self.iterator.next(isolation: actor) + } } } diff --git a/Sources/NIOCore/NIODecodedAsyncSequence.swift b/Sources/NIOCore/NIODecodedAsyncSequence.swift index 666498ae8a9..a40ace689b0 100644 --- a/Sources/NIOCore/NIODecodedAsyncSequence.swift +++ b/Sources/NIOCore/NIODecodedAsyncSequence.swift @@ -122,6 +122,7 @@ extension NIODecodedAsyncSequence: AsyncSequence { /// The same as `next(isolation:)` but not isolated to an actor, which allows /// for less availability restrictions. @inlinable + @concurrent public mutating func next() async throws -> Element? { while true { switch self.state { diff --git a/Sources/NIOFS/DirectoryEntries.swift b/Sources/NIOFS/DirectoryEntries.swift index 38267dc7440..a04da1e7a16 100644 --- a/Sources/NIOFS/DirectoryEntries.swift +++ b/Sources/NIOFS/DirectoryEntries.swift @@ -66,6 +66,7 @@ public struct DirectoryEntries: AsyncSequence, Sendable { self.currentBatch = [] } + @concurrent public mutating func next() async throws -> DirectoryEntry? { if self.currentBatch.isEmpty { let batch = try await self.iterator.next() @@ -74,6 +75,16 @@ public struct DirectoryEntries: AsyncSequence, Sendable { return self.currentBatch.popFirst() } + + @available(macOS 15.0, iOS 18.0, tvOS 18.0, watchOS 11.0, *) + public mutating func next(isolation actor: isolated (any Actor)?) async throws(any Error) -> DirectoryEntry? { + if self.currentBatch.isEmpty { + let batch = try await self.iterator.next(isolation: actor) + self.currentBatch = (batch ?? [])[...] + } + + return self.currentBatch.popFirst() + } } } @@ -128,9 +139,15 @@ extension DirectoryEntries { self.iterator = iterator } + @concurrent public mutating func next() async throws -> [DirectoryEntry]? { try await self.iterator.next() } + + @available(macOS 15.0, iOS 18.0, tvOS 18.0, watchOS 11.0, *) + public mutating func next(isolation actor: isolated (any Actor)?) async throws(any Error) -> [DirectoryEntry]? { + try await self.iterator.next(isolation: actor) + } } } } diff --git a/Sources/NIOFS/FileChunks.swift b/Sources/NIOFS/FileChunks.swift index 85871c731d6..cab1c225bb6 100644 --- a/Sources/NIOFS/FileChunks.swift +++ b/Sources/NIOFS/FileChunks.swift @@ -74,9 +74,15 @@ public struct FileChunks: AsyncSequence, Sendable { self.iterator = iterator } + @concurrent public mutating func next() async throws -> ByteBuffer? { try await self.iterator.next() } + + @available(macOS 15.0, iOS 18.0, tvOS 18.0, watchOS 11.0, *) + public mutating func next(isolation actor: isolated (any Actor)?) async throws(any Error) -> ByteBuffer? { + try await self.iterator.next(isolation: actor) + } } } diff --git a/Sources/NIOFS/Internal/BufferedOrAnyStream.swift b/Sources/NIOFS/Internal/BufferedOrAnyStream.swift index 9833bb3db02..14db63957fb 100644 --- a/Sources/NIOFS/Internal/BufferedOrAnyStream.swift +++ b/Sources/NIOFS/Internal/BufferedOrAnyStream.swift @@ -46,6 +46,7 @@ internal enum BufferedOrAnyStream.AsyncIterator) + @concurrent internal mutating func next() async throws -> Element? { let element: Element? switch self { @@ -59,6 +60,20 @@ internal enum BufferedOrAnyStream Element? { + let element: Element? + switch self { + case var .bufferedStream(iterator): + defer { self = .bufferedStream(iterator) } + element = try await iterator.next(isolation: actor) + case var .anyAsyncSequence(iterator): + defer { self = .anyAsyncSequence(iterator) } + element = try await iterator.next(isolation: actor) + } + return element + } + internal init(wrapping iterator: AsyncSequenceProducer.AsyncIterator) { self = .bufferedStream(iterator) } @@ -91,8 +106,14 @@ internal struct AnyAsyncSequence: AsyncSequence, Sendable { self.iterator = iterator } + @concurrent internal mutating func next() async throws -> Element? { try await self.iterator.next() as? Element } + + @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) + internal mutating func next(isolation actor: isolated (any Actor)?) async throws -> Element? { + try await self.iterator.next(isolation: actor) as? Element + } } } diff --git a/Sources/NIOPerformanceTester/main.swift b/Sources/NIOPerformanceTester/main.swift index ac741839f31..379927b07c3 100644 --- a/Sources/NIOPerformanceTester/main.swift +++ b/Sources/NIOPerformanceTester/main.swift @@ -88,7 +88,7 @@ public func measureAndPrint(desc: String, fn: () throws -> Int) rethrows { @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) public func measure(_ fn: () async throws -> Int) async rethrows -> [Double] { - func measureOne(_ fn: () async throws -> Int) async rethrows -> Double { + nonisolated(nonsending) func measureOne(_ fn: () async throws -> Int) async rethrows -> Double { let start = DispatchTime.now().uptimeNanoseconds _ = try await fn() let end = DispatchTime.now().uptimeNanoseconds diff --git a/Sources/_NIOFileSystem/Internal/BufferedOrAnyStream.swift b/Sources/_NIOFileSystem/Internal/BufferedOrAnyStream.swift index 7e307a16023..8bbb302f835 100644 --- a/Sources/_NIOFileSystem/Internal/BufferedOrAnyStream.swift +++ b/Sources/_NIOFileSystem/Internal/BufferedOrAnyStream.swift @@ -92,7 +92,13 @@ internal struct AnyAsyncSequence: AsyncSequence, Sendable { } internal mutating func next() async throws -> Element? { - try await self.iterator.next() as? Element + var box = UnsafeTransfer(self.iterator) + return try await box.wrappedValue.next() as? Element + } + + @available(macOS 15.0, iOS 18.0, tvOS 18.0, watchOS 11.0, *) + internal mutating func next(isolation actor: isolated (any Actor)?) async throws(any Error) -> Element? { + try await self.iterator.next(isolation: actor) as? Element } } } diff --git a/Tests/NIOCoreTests/AsyncChannel/AsyncChannelTests.swift b/Tests/NIOCoreTests/AsyncChannel/AsyncChannelTests.swift index 62d798ea81e..3610269dbb6 100644 --- a/Tests/NIOCoreTests/AsyncChannel/AsyncChannelTests.swift +++ b/Tests/NIOCoreTests/AsyncChannel/AsyncChannelTests.swift @@ -180,6 +180,7 @@ final class AsyncChannelTests: XCTestCase { } } + @available(macOS 15.0, iOS 18.0, tvOS 18.0, watchOS 11.0, *) func testErrorsArePropagatedButAfterReads() async throws { let channel = NIOAsyncTestingChannel() let wrapped = try await channel.testingEventLoop.executeInContext { @@ -196,7 +197,7 @@ final class AsyncChannelTests: XCTestCase { let first = try await iterator.next() XCTAssertEqual(first, "hello") - try await XCTAssertThrowsError(await iterator.next()) { error in + try await XCTAssertThrowsError(await iterator.next(isolation: #isolation)) { error in XCTAssertEqual(error as? TestError, .bang) } } @@ -267,6 +268,7 @@ final class AsyncChannelTests: XCTestCase { try await channel.closeIgnoringSuppression() } + @available(macOS 15.0, iOS 18.0, tvOS 18.0, watchOS 11.0, *) func testManagingBackPressure() async throws { let channel = NIOAsyncTestingChannel() let readCounter = ReadCounter() @@ -330,13 +332,13 @@ final class AsyncChannelTests: XCTestCase { // Now consume three elements from the pipeline. This should not unbuffer the read, as 3 elements remain. var reader = inbound.makeAsyncIterator() for _ in 0..<3 { - try await XCTAsyncAssertNotNil(await reader.next()) + try await XCTAsyncAssertNotNil(await reader.next(isolation: #isolation)) } await channel.testingEventLoop.run() XCTAssertEqual(readCounter.readCount, 6) // Removing the next element should trigger an automatic read. - try await XCTAsyncAssertNotNil(await reader.next()) + try await XCTAsyncAssertNotNil(await reader.next(isolation: #isolation)) await channel.testingEventLoop.run() XCTAssertEqual(readCounter.readCount, 7) @@ -366,7 +368,7 @@ final class AsyncChannelTests: XCTestCase { // This time we'll consume 4 more elements, and we won't find a read at all. for _ in 0..<4 { - try await XCTAsyncAssertNotNil(await reader.next()) + try await XCTAsyncAssertNotNil(await reader.next(isolation: #isolation)) } await channel.testingEventLoop.run() XCTAssertEqual(readCounter.readCount, 13) diff --git a/Tests/NIOCoreTests/AsyncSequences/NIOAsyncSequenceTests.swift b/Tests/NIOCoreTests/AsyncSequences/NIOAsyncSequenceTests.swift index 2def03562a9..a1c6f9d9182 100644 --- a/Tests/NIOCoreTests/AsyncSequences/NIOAsyncSequenceTests.swift +++ b/Tests/NIOCoreTests/AsyncSequences/NIOAsyncSequenceTests.swift @@ -762,6 +762,7 @@ private func XCTAssertEqualWithoutAutoclosure( @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) extension AsyncSequence { /// Collect all elements in the sequence into an array. + @concurrent fileprivate func collect() async rethrows -> [Element] { try await self.reduce(into: []) { accumulated, next in accumulated.append(next) diff --git a/Tests/NIOCoreTests/AsyncSequences/NIOThrowingAsyncSequenceTests.swift b/Tests/NIOCoreTests/AsyncSequences/NIOThrowingAsyncSequenceTests.swift index 888361a82eb..af10080d428 100644 --- a/Tests/NIOCoreTests/AsyncSequences/NIOThrowingAsyncSequenceTests.swift +++ b/Tests/NIOCoreTests/AsyncSequences/NIOThrowingAsyncSequenceTests.swift @@ -386,13 +386,12 @@ final class NIOThrowingAsyncSequenceProducerTests: XCTestCase { var elements = [Int]() - await XCTAssertThrowsError( - try await { - for try await element in self.sequence { - elements.append(element) - } - }() - ) { error in + do { + for try await element in self.sequence { + elements.append(element) + } + XCTFail("Expected that an error is thrown in the loop above") + } catch { XCTAssertEqual(error as? ChannelError, .alreadyClosed) } @@ -923,6 +922,7 @@ private func XCTAssertEqualWithoutAutoclosure( @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) extension AsyncSequence { /// Collect all elements in the sequence into an array. + @concurrent fileprivate func collect() async rethrows -> [Element] { try await self.reduce(into: []) { accumulated, next in accumulated.append(next) diff --git a/Tests/NIOCoreTests/XCTest+AsyncAwait.swift b/Tests/NIOCoreTests/XCTest+AsyncAwait.swift index 0e7987d729f..9df3a88cae2 100644 --- a/Tests/NIOCoreTests/XCTest+AsyncAwait.swift +++ b/Tests/NIOCoreTests/XCTest+AsyncAwait.swift @@ -42,7 +42,7 @@ import XCTest @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -internal func XCTAssertThrowsError( +nonisolated(nonsending) internal func XCTAssertThrowsError( _ expression: @autoclosure () async throws -> T, file: StaticString = #filePath, line: UInt = #line, @@ -57,7 +57,7 @@ internal func XCTAssertThrowsError( } @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -internal func XCTAssertNoThrow( +nonisolated(nonsending) internal func XCTAssertNoThrow( _ expression: @autoclosure () async throws -> T, file: StaticString = #filePath, line: UInt = #line @@ -70,7 +70,7 @@ internal func XCTAssertNoThrow( } @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -internal func XCTAssertNoThrowWithResult( +nonisolated(nonsending) internal func XCTAssertNoThrowWithResult( _ expression: @autoclosure () async throws -> Result, file: StaticString = #filePath, line: UInt = #line @@ -84,7 +84,7 @@ internal func XCTAssertNoThrowWithResult( } @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -internal func XCTAsyncAssertNotNil( +nonisolated(nonsending) internal func XCTAsyncAssertNotNil( _ expression: @autoclosure () async throws -> Any?, file: StaticString = #filePath, line: UInt = #line @@ -94,7 +94,7 @@ internal func XCTAsyncAssertNotNil( } @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -internal func XCTAsyncAssertNil( +nonisolated(nonsending) internal func XCTAsyncAssertNil( _ expression: @autoclosure () async throws -> Any?, file: StaticString = #filePath, line: UInt = #line diff --git a/Tests/NIOEmbeddedTests/XCTest+AsyncAwait.swift b/Tests/NIOEmbeddedTests/XCTest+AsyncAwait.swift index e91c4a5a20b..f3684c32f32 100644 --- a/Tests/NIOEmbeddedTests/XCTest+AsyncAwait.swift +++ b/Tests/NIOEmbeddedTests/XCTest+AsyncAwait.swift @@ -42,6 +42,7 @@ import XCTest @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) +nonisolated(nonsending) internal func XCTAssertThrowsError( _ expression: @autoclosure () async throws -> T, file: StaticString = #filePath, @@ -57,6 +58,7 @@ internal func XCTAssertThrowsError( } @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) +nonisolated(nonsending) internal func XCTAssertNoThrowWithResult( _ expression: @autoclosure () async throws -> Result, file: StaticString = #filePath, diff --git a/Tests/NIOFSIntegrationTests/FileHandleTests.swift b/Tests/NIOFSIntegrationTests/FileHandleTests.swift index 6de9b6f9c23..2f330f3f5ec 100644 --- a/Tests/NIOFSIntegrationTests/FileHandleTests.swift +++ b/Tests/NIOFSIntegrationTests/FileHandleTests.swift @@ -699,10 +699,12 @@ final class FileHandleTests: XCTestCase { try await self.testCloseOrDetachMidRead(close: false) } + @available(macOS 15.0, iOS 18.0, tvOS 18.0, watchOS 11.0, *) func testCloseBeforeReadingFromFile() async throws { try await self.testCloseOrDetachBeforeRead(close: true) } + @available(macOS 15.0, iOS 18.0, tvOS 18.0, watchOS 11.0, *) func testDetachBeforeReadingFromFile() async throws { try await self.testCloseOrDetachBeforeRead(close: false) } @@ -748,6 +750,7 @@ final class FileHandleTests: XCTestCase { } } + @available(macOS 15.0, iOS 18.0, tvOS 18.0, watchOS 11.0, *) private func testCloseOrDetachBeforeRead(close: Bool) async throws { try await self.withHandle(forFileAtPath: Self.thisFile, autoClose: false) { handle in if close { @@ -759,7 +762,7 @@ final class FileHandleTests: XCTestCase { var iterator = handle.readChunks().makeAsyncIterator() await XCTAssertThrowsFileSystemErrorAsync { - try await iterator.next() + try await iterator.next(isolation: #isolation) } onError: { error in XCTAssertEqual(error.code, .closed) } diff --git a/Tests/NIOFSIntegrationTests/FileSystemTests.swift b/Tests/NIOFSIntegrationTests/FileSystemTests.swift index b813c91bcbd..80d85618481 100644 --- a/Tests/NIOFSIntegrationTests/FileSystemTests.swift +++ b/Tests/NIOFSIntegrationTests/FileSystemTests.swift @@ -1230,22 +1230,22 @@ final class FileSystemTests: XCTestCase { } func testReplaceFile(_ existingType: FileType?, with replacementType: FileType) async throws { - func makeRegularFile(at path: NIOFilePath) async throws { + nonisolated(nonsending) func makeRegularFile(at path: NIOFilePath) async throws { try await self.fs.withFileHandle( forWritingAt: path, options: .newFile(replaceExisting: false) ) { _ in } } - func makeSymbolicLink(at path: NIOFilePath) async throws { + nonisolated(nonsending) func makeSymbolicLink(at path: NIOFilePath) async throws { try await self.fs.createSymbolicLink(at: path, withDestination: "/whatever") } - func makeDirectory(at path: NIOFilePath) async throws { + nonisolated(nonsending) func makeDirectory(at path: NIOFilePath) async throws { try await self.fs.createDirectory(at: path, withIntermediateDirectories: true) } - func makeFile(ofType type: FileType, at path: NIOFilePath) async throws { + nonisolated(nonsending) func makeFile(ofType type: FileType, at path: NIOFilePath) async throws { switch type { case .regular: try await makeRegularFile(at: path) @@ -1424,7 +1424,7 @@ final class FileSystemTests: XCTestCase { @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) extension FileSystemTests { private func checkDirectoriesMatch(_ root1: NIOFilePath, _ root2: NIOFilePath) async throws { - func namesAndTypes(_ root: NIOFilePath) async throws -> [(String, FileType)] { + nonisolated(nonsending) func namesAndTypes(_ root: NIOFilePath) async throws -> [(String, FileType)] { try await self.fs.withDirectoryHandle(atPath: root) { dir in try await dir.listContents() .reduce(into: []) { $0.append($1) } diff --git a/Tests/NIOFSTests/Internal/Concurrency Primitives/BufferedStreamTests.swift b/Tests/NIOFSTests/Internal/Concurrency Primitives/BufferedStreamTests.swift index 8f98722d84b..d1b7182b009 100644 --- a/Tests/NIOFSTests/Internal/Concurrency Primitives/BufferedStreamTests.swift +++ b/Tests/NIOFSTests/Internal/Concurrency Primitives/BufferedStreamTests.swift @@ -1081,7 +1081,7 @@ final class BufferedStreamTests: XCTestCase { @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) extension AsyncSequence { /// Collect all elements in the sequence into an array. - fileprivate func collect() async rethrows -> [Element] { + @concurrent fileprivate func collect() async rethrows -> [Element] { try await self.reduce(into: []) { accumulated, next in accumulated.append(next) } diff --git a/Tests/NIOPosixTests/AsyncChannelBootstrapTests.swift b/Tests/NIOPosixTests/AsyncChannelBootstrapTests.swift index f8a09a76109..9b3fd206537 100644 --- a/Tests/NIOPosixTests/AsyncChannelBootstrapTests.swift +++ b/Tests/NIOPosixTests/AsyncChannelBootstrapTests.swift @@ -198,7 +198,7 @@ private final class AddressedEnvelopingHandler: ChannelDuplexHandler { } } -@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *) +@available(macOS 15.0, iOS 18.0, tvOS 18.0, watchOS 11.0, *) final class AsyncChannelBootstrapTests: XCTestCase { var group: MultiThreadedEventLoopGroup! @@ -266,7 +266,7 @@ final class AsyncChannelBootstrapTests: XCTestCase { try await outbound.write("hello") } - await XCTAsyncAssertEqual(await iterator.next(), .string("hello")) + await XCTAsyncAssertEqual(await iterator.next(isolation: #isolation), .string("hello")) group.cancelAll() } @@ -330,7 +330,7 @@ final class AsyncChannelBootstrapTests: XCTestCase { // This is the actual content try await outbound.write("hello") } - await XCTAsyncAssertEqual(await serverIterator.next(), .string("hello")) + await XCTAsyncAssertEqual(await serverIterator.next(isolation: #isolation), .string("hello")) case .byte: preconditionFailure() } @@ -349,7 +349,7 @@ final class AsyncChannelBootstrapTests: XCTestCase { // This is the actual content try await outbound.write(UInt8(8)) } - await XCTAsyncAssertEqual(await serverIterator.next(), .byte(8)) + await XCTAsyncAssertEqual(await serverIterator.next(isolation: #isolation), .byte(8)) } group.cancelAll() @@ -412,7 +412,7 @@ final class AsyncChannelBootstrapTests: XCTestCase { // This is the actual content try await outbound.write("hello") } - await XCTAsyncAssertEqual(await serverIterator.next(), .string("hello")) + await XCTAsyncAssertEqual(await serverIterator.next(isolation: #isolation), .string("hello")) case .byte: preconditionFailure() } @@ -429,7 +429,7 @@ final class AsyncChannelBootstrapTests: XCTestCase { // This is the actual content try await outbound.write("hello") } - await XCTAsyncAssertEqual(await serverIterator.next(), .string("hello")) + await XCTAsyncAssertEqual(await serverIterator.next(isolation: #isolation), .string("hello")) case .byte: preconditionFailure() } @@ -448,7 +448,7 @@ final class AsyncChannelBootstrapTests: XCTestCase { // This is the actual content try await outbound.write(UInt8(8)) } - await XCTAsyncAssertEqual(await serverIterator.next(), .byte(8)) + await XCTAsyncAssertEqual(await serverIterator.next(isolation: #isolation), .byte(8)) } let stringByteNegotiationResult = try await self.makeClientChannelWithNestedProtocolNegotiation( @@ -465,7 +465,7 @@ final class AsyncChannelBootstrapTests: XCTestCase { // This is the actual content try await outbound.write(UInt8(8)) } - await XCTAsyncAssertEqual(await serverIterator.next(), .byte(8)) + await XCTAsyncAssertEqual(await serverIterator.next(isolation: #isolation), .byte(8)) } group.cancelAll() @@ -563,7 +563,7 @@ final class AsyncChannelBootstrapTests: XCTestCase { // This is the actual content try await outbound.write("hello") } - await XCTAsyncAssertEqual(await serverIterator.next(), .string("hello")) + await XCTAsyncAssertEqual(await serverIterator.next(isolation: #isolation), .string("hello")) case .byte: preconditionFailure() } @@ -654,7 +654,7 @@ final class AsyncChannelBootstrapTests: XCTestCase { try await outbound.write("hello") } - await XCTAsyncAssertEqual(await iterator.next(), .string("hello")) + await XCTAsyncAssertEqual(await iterator.next(isolation: #isolation), .string("hello")) group.cancelAll() } @@ -676,10 +676,10 @@ final class AsyncChannelBootstrapTests: XCTestCase { var clientInboundIterator = clientChannelInbound.makeAsyncIterator() try await clientChannelOutbound.write("request") - try await XCTAsyncAssertEqual(try await serverInboundIterator.next(), "request") + try await XCTAsyncAssertEqual(try await serverInboundIterator.next(isolation: #isolation), "request") try await serverChannelOutbound.write("response") - try await XCTAsyncAssertEqual(try await clientInboundIterator.next(), "response") + try await XCTAsyncAssertEqual(try await clientInboundIterator.next(isolation: #isolation), "response") } } } @@ -731,10 +731,10 @@ final class AsyncChannelBootstrapTests: XCTestCase { var secondInboundIterator = secondChannelInbound.makeAsyncIterator() try await firstChannelOutbound.write("request") - try await XCTAsyncAssertEqual(try await secondInboundIterator.next(), "request") + try await XCTAsyncAssertEqual(try await secondInboundIterator.next(isolation: #isolation), "request") try await secondChannelOutbound.write("response") - try await XCTAsyncAssertEqual(try await firstInboundIterator.next(), "response") + try await XCTAsyncAssertEqual(try await firstInboundIterator.next(isolation: #isolation), "response") } } @@ -829,11 +829,11 @@ final class AsyncChannelBootstrapTests: XCTestCase { var fromChannelInboundIterator = fromChannelInbound.makeAsyncIterator() try await toChannelOutbound.write(.init(string: "Request")) - try await XCTAsyncAssertEqual(try await inboundIterator.next(), ByteBuffer(string: "Request")) + try await XCTAsyncAssertEqual(try await inboundIterator.next(isolation: #isolation), ByteBuffer(string: "Request")) let response = ByteBuffer(string: "Response") try await channelOutbound.write(response) - try await XCTAsyncAssertEqual(try await fromChannelInboundIterator.next(), response) + try await XCTAsyncAssertEqual(try await fromChannelInboundIterator.next(isolation: #isolation), response) } } } @@ -882,11 +882,11 @@ final class AsyncChannelBootstrapTests: XCTestCase { var inboundIterator = channelInbound.makeAsyncIterator() var fromChannelInboundIterator = fromChannelInbound.makeAsyncIterator() - try await XCTAsyncAssertEqual(try await inboundIterator.next(), nil) + try await XCTAsyncAssertEqual(try await inboundIterator.next(isolation: #isolation), nil) let response = ByteBuffer(string: "Response") try await channelOutbound.write(response) - try await XCTAsyncAssertEqual(try await fromChannelInboundIterator.next(), response) + try await XCTAsyncAssertEqual(try await fromChannelInboundIterator.next(isolation: #isolation), response) } } } @@ -935,7 +935,7 @@ final class AsyncChannelBootstrapTests: XCTestCase { var inboundIterator = channelInbound.makeAsyncIterator() try await toChannelOutbound.write(.init(string: "Request")) - try await XCTAsyncAssertEqual(try await inboundIterator.next(), ByteBuffer(string: "Request")) + try await XCTAsyncAssertEqual(try await inboundIterator.next(isolation: #isolation), ByteBuffer(string: "Request")) let response = ByteBuffer(string: "Response") await XCTAsyncAssertThrowsError(try await channelOutbound.write(response)) { error in @@ -1011,11 +1011,11 @@ final class AsyncChannelBootstrapTests: XCTestCase { try await channel.executeThenClose { channelInbound, channelOutbound in var inboundIterator = channelInbound.makeAsyncIterator() do { - try await XCTAsyncAssertEqual(try await inboundIterator.next(), "Hello") + try await XCTAsyncAssertEqual(try await inboundIterator.next(isolation: #isolation), "Hello") let expectedResponse = ByteBuffer(string: "Response\n") try await channelOutbound.write("Response") - let response = try await fromChannelInboundIterator.next() + let response = try await fromChannelInboundIterator.next(isolation: #isolation) XCTAssertEqual(response, expectedResponse) } catch { // We only got to close the FDs that are not owned by the PipeChannel @@ -1109,11 +1109,11 @@ final class AsyncChannelBootstrapTests: XCTestCase { var fromChannelInboundIterator = fromChannelInbound.makeAsyncIterator() try await toChannelOutbound.write(.init(string: "Request")) - try await XCTAsyncAssertEqual(try await inboundIterator.next(), ByteBuffer(string: "Request")) + try await XCTAsyncAssertEqual(try await inboundIterator.next(isolation: #isolation), ByteBuffer(string: "Request")) let response = ByteBuffer(string: "Response") try await channelOutbound.write(response) - try await XCTAsyncAssertEqual(try await fromChannelInboundIterator.next(), response) + try await XCTAsyncAssertEqual(try await fromChannelInboundIterator.next(isolation: #isolation), response) } } } @@ -1173,11 +1173,11 @@ final class AsyncChannelBootstrapTests: XCTestCase { var inboundIterator = channelInbound.makeAsyncIterator() var fromChannelInboundIterator = fromChannelInbound.makeAsyncIterator() - try await XCTAsyncAssertEqual(try await inboundIterator.next(), nil) + try await XCTAsyncAssertEqual(try await inboundIterator.next(isolation: #isolation), nil) let response = ByteBuffer(string: "Response") try await channelOutbound.write(response) - try await XCTAsyncAssertEqual(try await fromChannelInboundIterator.next(), response) + try await XCTAsyncAssertEqual(try await fromChannelInboundIterator.next(isolation: #isolation), response) } } @@ -1237,7 +1237,7 @@ final class AsyncChannelBootstrapTests: XCTestCase { var inboundIterator = channelInbound.makeAsyncIterator() try await toChannelOutbound.write(.init(string: "Request")) - try await XCTAsyncAssertEqual(try await inboundIterator.next(), ByteBuffer(string: "Request")) + try await XCTAsyncAssertEqual(try await inboundIterator.next(isolation: #isolation), ByteBuffer(string: "Request")) let response = ByteBuffer(string: "Response") await XCTAsyncAssertThrowsError(try await channelOutbound.write(response)) { error in @@ -1264,10 +1264,10 @@ final class AsyncChannelBootstrapTests: XCTestCase { var clientInboundIterator = clientChannelInbound.makeAsyncIterator() try await clientChannelOutbound.write("request") - try await XCTAsyncAssertEqual(try await serverInboundIterator.next(), "request") + try await XCTAsyncAssertEqual(try await serverInboundIterator.next(isolation: #isolation), "request") try await serverChannelOutbound.write("response") - try await XCTAsyncAssertEqual(try await clientInboundIterator.next(), "response") + try await XCTAsyncAssertEqual(try await clientInboundIterator.next(isolation: #isolation), "response") } } } @@ -1305,10 +1305,10 @@ final class AsyncChannelBootstrapTests: XCTestCase { var secondInboundIterator = secondChannelInbound.makeAsyncIterator() try await firstChannelOutbound.write("request") - try await XCTAsyncAssertEqual(try await secondInboundIterator.next(), "request") + try await XCTAsyncAssertEqual(try await secondInboundIterator.next(isolation: #isolation), "request") try await secondChannelOutbound.write("response") - try await XCTAsyncAssertEqual(try await firstInboundIterator.next(), "response") + try await XCTAsyncAssertEqual(try await firstInboundIterator.next(isolation: #isolation), "response") } } @@ -1375,7 +1375,7 @@ final class AsyncChannelBootstrapTests: XCTestCase { try await outbound.write("hello") } - await XCTAsyncAssertEqual(await iterator.next(), .string("hello")) + await XCTAsyncAssertEqual(await iterator.next(isolation: #isolation), .string("hello")) group.cancelAll() } diff --git a/Tests/NIOPosixTests/NIOScheduledCallbackTests.swift b/Tests/NIOPosixTests/NIOScheduledCallbackTests.swift index 4caa11f6229..572361c40bf 100644 --- a/Tests/NIOPosixTests/NIOScheduledCallbackTests.swift +++ b/Tests/NIOPosixTests/NIOScheduledCallbackTests.swift @@ -459,7 +459,7 @@ func XCTWithTimeout( _ timeout: TimeAmount, file: StaticString = #filePath, line: UInt = #line, - operation: @escaping @Sendable () async throws -> Result + operation: @isolated(any) @escaping @Sendable () async throws -> Result ) async throws -> Result where Result: Sendable { do { return try await withTimeout(timeout, operation: operation) @@ -471,7 +471,7 @@ func XCTWithTimeout( func withTimeout( _ timeout: TimeAmount, - operation: @escaping @Sendable () async throws -> Result + operation: @isolated(any) @escaping @Sendable () async throws -> Result ) async throws -> Result where Result: Sendable { try await withThrowingTaskGroup(of: Result.self) { group in group.addTask { diff --git a/Tests/NIOPosixTests/XCTest+AsyncAwait.swift b/Tests/NIOPosixTests/XCTest+AsyncAwait.swift index 2c88cdb9bc3..9dd8fb5a47a 100644 --- a/Tests/NIOPosixTests/XCTest+AsyncAwait.swift +++ b/Tests/NIOPosixTests/XCTest+AsyncAwait.swift @@ -42,7 +42,7 @@ import XCTest @available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) internal func XCTAssertThrowsError( - _ expression: () async throws -> T, + _ expression: nonisolated(nonsending) () async throws -> T, file: StaticString = #filePath, line: UInt = #line, verify: (Error) -> Void = { _ in }