From 1c700a64f1dd237f33b59482f81806e6f7697f52 Mon Sep 17 00:00:00 2001 From: Fabian Fett Date: Wed, 29 May 2024 14:35:02 +0200 Subject: [PATCH 1/5] Add TaskExecutor conformance to EventLoops --- .../NIOCore/EventLoop+SerialExecutor.swift | 38 ++++++++++++-- Sources/NIOCore/EventLoop.swift | 9 +++- Sources/NIOPosix/SelectableEventLoop.swift | 6 +++ Tests/NIOPosixTests/TaskExecutorTests.swift | 51 +++++++++++++++++++ 4 files changed, 99 insertions(+), 5 deletions(-) create mode 100644 Tests/NIOPosixTests/TaskExecutorTests.swift diff --git a/Sources/NIOCore/EventLoop+SerialExecutor.swift b/Sources/NIOCore/EventLoop+SerialExecutor.swift index f157701778..6453452bde 100644 --- a/Sources/NIOCore/EventLoop+SerialExecutor.swift +++ b/Sources/NIOCore/EventLoop+SerialExecutor.swift @@ -51,7 +51,7 @@ extension NIOSerialEventLoopExecutor { /// This type is not recommended for use because it risks problems with unowned /// executors. Adopters are recommended to conform their own event loop /// types to `SerialExecutor`. -final class NIODefaultSerialEventLoopExecutor { +final class NIODefaultEventLoopExecutor { @usableFromInline let loop: EventLoop @@ -62,7 +62,7 @@ final class NIODefaultSerialEventLoopExecutor { } @available(macOS 14.0, iOS 17.0, watchOS 10.0, tvOS 17.0, *) -extension NIODefaultSerialEventLoopExecutor: SerialExecutor { +extension NIODefaultEventLoopExecutor: SerialExecutor { @inlinable public func enqueue(_ job: consuming ExecutorJob) { self.loop.enqueue(job) @@ -71,12 +71,42 @@ extension NIODefaultSerialEventLoopExecutor: SerialExecutor { @inlinable public func asUnownedSerialExecutor() -> UnownedSerialExecutor { UnownedSerialExecutor(complexEquality: self) - } @inlinable - public func isSameExclusiveExecutionContext(other: NIODefaultSerialEventLoopExecutor) -> Bool { + public func isSameExclusiveExecutionContext(other: NIODefaultEventLoopExecutor) -> Bool { self.loop === other.loop } } #endif + +#if compiler(>=6.0) +/// A helper protocol that can be mixed in to a NIO ``EventLoop`` to provide an +/// automatic conformance to `TaskExecutor`. +/// +/// Implementers of `EventLoop` should consider conforming to this protocol as +/// well on Swift 6.0 and later. +@available(macOS 9999.0, iOS 9999.0, watchOS 9999.0, tvOS 9999.0, *) +public protocol NIOTaskEventLoopExecutor: NIOSerialEventLoopExecutor & TaskExecutor { } + +@available(macOS 9999.0, iOS 9999.0, watchOS 9999.0, tvOS 9999.0, *) +extension NIOTaskEventLoopExecutor { + @inlinable + func asUnownedTaskExecutor() -> UnownedTaskExecutor { + UnownedTaskExecutor(ordinary: self) + } + + @inlinable + public var taskExecutor: any TaskExecutor { + self + } +} + +@available(macOS 9999.0, iOS 9999.0, watchOS 9999.0, tvOS 9999.0, *) +extension NIODefaultEventLoopExecutor: TaskExecutor { + @inlinable + public func asUnownedTaskExecutor() -> UnownedTaskExecutor { + UnownedTaskExecutor(ordinary: self) + } +} +#endif diff --git a/Sources/NIOCore/EventLoop.swift b/Sources/NIOCore/EventLoop.swift index 50b90ed925..45dfa8ff52 100644 --- a/Sources/NIOCore/EventLoop.swift +++ b/Sources/NIOCore/EventLoop.swift @@ -383,7 +383,7 @@ extension EventLoop { #if compiler(>=5.9) @available(macOS 14.0, iOS 17.0, watchOS 10.0, tvOS 17.0, *) public var executor: any SerialExecutor { - NIODefaultSerialEventLoopExecutor(self) + NIODefaultEventLoopExecutor(self) } @inlinable @@ -398,6 +398,13 @@ extension EventLoop { } } #endif + + #if compiler(>=6.0) + @available(macOS 9999.0, iOS 9999.0, watchOS 9999.0, tvOS 9999.0, *) + public var taskExecutor: any TaskExecutor { + NIODefaultEventLoopExecutor(self) + } + #endif } extension EventLoopGroup { diff --git a/Sources/NIOPosix/SelectableEventLoop.swift b/Sources/NIOPosix/SelectableEventLoop.swift index d262cf56ad..da32500213 100644 --- a/Sources/NIOPosix/SelectableEventLoop.swift +++ b/Sources/NIOPosix/SelectableEventLoop.swift @@ -883,3 +883,9 @@ internal func assertExpression(_ body: () -> Bool) { return body() }()) } + +// MARK: TaskExecutor conformance +#if compiler(>=6.0) +@available(macOS 9999.0, iOS 9999.0, watchOS 9999.0, tvOS 9999.0, *) +extension SelectableEventLoop: NIOTaskEventLoopExecutor { } +#endif diff --git a/Tests/NIOPosixTests/TaskExecutorTests.swift b/Tests/NIOPosixTests/TaskExecutorTests.swift new file mode 100644 index 0000000000..34b316c528 --- /dev/null +++ b/Tests/NIOPosixTests/TaskExecutorTests.swift @@ -0,0 +1,51 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftNIO open source project +// +// Copyright (c) 2024 Apple Inc. and the SwiftNIO project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of SwiftNIO project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// +import NIOCore +import NIOEmbedded +import NIOPosix +import XCTest + +final class TaskExecutorTests: XCTestCase { + @available(macOS 9999.0, iOS 9999.0, watchOS 9999.0, tvOS 9999.0, *) + func testBasicExecutorFitsOnEventLoop_MTELG() async throws { + #if compiler(>=6.0) + let group = MultiThreadedEventLoopGroup(numberOfThreads: 2) + defer { + try! group.syncShutdownGracefully() + } + let loops = Array(group.makeIterator()) + await withTaskGroup(of: Void.self) { taskGroup in + taskGroup.addTask(executorPreference: loops[0].taskExecutor) { + loops[0].assertInEventLoop() + loops[1].assertNotInEventLoop() + + withUnsafeCurrentTask { task in + // this currently fails on macOS + XCTAssertEqual(task?.unownedTaskExecutor, loops[0].taskExecutor.asUnownedTaskExecutor()) + } + } + + taskGroup.addTask(executorPreference: loops[1].taskExecutor) { + loops[0].assertNotInEventLoop() + loops[1].assertInEventLoop() + + withUnsafeCurrentTask { task in + // this currently fails on macOS + XCTAssertEqual(task?.unownedTaskExecutor, loops[1].taskExecutor.asUnownedTaskExecutor()) + } + } + } + #endif + } +} From 31f491c49f1c4a9fd5f70e7c7e7a2ae9729a8a79 Mon Sep 17 00:00:00 2001 From: Fabian Fett Date: Wed, 29 May 2024 19:56:54 +0200 Subject: [PATCH 2/5] Code review --- .../NIOPosixBenchmarks/Benchmarks.swift | 35 +++++++-- .../NIOEmbedded/AsyncTestingEventLoop.swift | 6 ++ Sources/NIOEmbedded/Embedded.swift | 7 ++ Tests/NIOPosixTests/TaskExecutorTests.swift | 77 ++++++++++++++++--- 4 files changed, 108 insertions(+), 17 deletions(-) diff --git a/Benchmarks/Benchmarks/NIOPosixBenchmarks/Benchmarks.swift b/Benchmarks/Benchmarks/NIOPosixBenchmarks/Benchmarks.swift index 45ddfc2a3f..825fb9e576 100644 --- a/Benchmarks/Benchmarks/NIOPosixBenchmarks/Benchmarks.swift +++ b/Benchmarks/Benchmarks/NIOPosixBenchmarks/Benchmarks.swift @@ -20,18 +20,18 @@ private let eventLoop = MultiThreadedEventLoopGroup.singleton.next() let benchmarks = { let defaultMetrics: [BenchmarkMetric] = [ .mallocCountTotal, + .cpuTotal ] Benchmark( "TCPEcho", configuration: .init( metrics: defaultMetrics, - timeUnits: .milliseconds, - scalingFactor: .mega + scalingFactor: .one ) ) { benchmark in try runTCPEcho( - numberOfWrites: benchmark.scaledIterations.upperBound, + numberOfWrites: 1_000_000, eventLoop: eventLoop ) } @@ -40,11 +40,10 @@ let benchmarks = { // to serial executor is also gated behind 5.9. #if compiler(>=5.9) Benchmark( - "TCPEchoAsyncChannel", + "TCPEchoAsyncChannel using globalHook 1M times", configuration: .init( metrics: defaultMetrics, - timeUnits: .milliseconds, - scalingFactor: .mega, + scalingFactor: .one, // We are expecting a bit of allocation variance due to an allocation // in the Concurrency runtime which happens when resuming a continuation. thresholds: [.mallocCountTotal: .init(absolute: [.p90: 2000])], @@ -59,9 +58,31 @@ let benchmarks = { ) ) { benchmark in try await runTCPEchoAsyncChannel( - numberOfWrites: benchmark.scaledIterations.upperBound, + numberOfWrites: 1_000_000, eventLoop: eventLoop ) } #endif + + #if compiler(>=6.0) + if #available(macOS 15.0, *) { + Benchmark( + "TCPEchoAsyncChannel using task executor preference", + configuration: .init( + metrics: defaultMetrics, + scalingFactor: .one + // We are expecting a bit of allocation variance due to an allocation + // in the Concurrency runtime which happens when resuming a continuation. +// thresholds: [.mallocCountTotal: .init(absolute: [.p90: 2000])] + ) + ) { benchmark in + try await withTaskExecutorPreference(eventLoop.taskExecutor) { + try await runTCPEchoAsyncChannel( + numberOfWrites: 1_000_000, + eventLoop: eventLoop + ) + } + } + } + #endif } diff --git a/Sources/NIOEmbedded/AsyncTestingEventLoop.swift b/Sources/NIOEmbedded/AsyncTestingEventLoop.swift index bdc9423c85..58f20b6bd0 100644 --- a/Sources/NIOEmbedded/AsyncTestingEventLoop.swift +++ b/Sources/NIOEmbedded/AsyncTestingEventLoop.swift @@ -355,6 +355,12 @@ public final class NIOAsyncTestingEventLoop: EventLoop, @unchecked Sendable { extension NIOAsyncTestingEventLoop: NIOSerialEventLoopExecutor { } #endif +// MARK: TaskExecutor conformance +#if compiler(>=6.0) +@available(macOS 9999.0, iOS 9999.0, watchOS 9999.0, tvOS 9999.0, *) +extension NIOAsyncTestingEventLoop: NIOTaskEventLoopExecutor { } +#endif + /// This is a thread-safe promise creation store. /// /// We use this to keep track of where promises come from in the `NIOAsyncTestingEventLoop`. diff --git a/Sources/NIOEmbedded/Embedded.swift b/Sources/NIOEmbedded/Embedded.swift index 4a8ce0d218..9e2eb23cdf 100644 --- a/Sources/NIOEmbedded/Embedded.swift +++ b/Sources/NIOEmbedded/Embedded.swift @@ -240,6 +240,13 @@ public final class EmbeddedEventLoop: EventLoop { fatalError("EmbeddedEventLoop is not thread safe and cannot be used as a SerialExecutor. Use NIOAsyncTestingEventLoop instead.") } #endif + + #if compiler(>=6.0) + @available(macOS 9999.0, iOS 9999.0, watchOS 9999.0, tvOS 9999.0, *) + public var taskExecutor: any TaskExecutor { + fatalError("EmbeddedEventLoop is not thread safe and cannot be used as a TaskExecutor. Use NIOAsyncTestingEventLoop instead.") + } + #endif } @usableFromInline diff --git a/Tests/NIOPosixTests/TaskExecutorTests.swift b/Tests/NIOPosixTests/TaskExecutorTests.swift index 34b316c528..bd8cf4bc84 100644 --- a/Tests/NIOPosixTests/TaskExecutorTests.swift +++ b/Tests/NIOPosixTests/TaskExecutorTests.swift @@ -17,32 +17,89 @@ import NIOPosix import XCTest final class TaskExecutorTests: XCTestCase { + + #if compiler(>=6.0) @available(macOS 9999.0, iOS 9999.0, watchOS 9999.0, tvOS 9999.0, *) - func testBasicExecutorFitsOnEventLoop_MTELG() async throws { + func _runTests(loop1: some EventLoop, loop2: some EventLoop) async { + await withTaskGroup(of: Void.self) { taskGroup in + taskGroup.addTask(executorPreference: loop1.taskExecutor) { + loop1.assertInEventLoop() + loop2.assertNotInEventLoop() + + withUnsafeCurrentTask { task in + // this currently fails on macOS + XCTAssertEqual(task?.unownedTaskExecutor, loop1.taskExecutor.asUnownedTaskExecutor()) + } + } + + taskGroup.addTask(executorPreference: loop2.taskExecutor) { + loop1.assertNotInEventLoop() + loop2.assertInEventLoop() + + withUnsafeCurrentTask { task in + // this currently fails on macOS + XCTAssertEqual(task?.unownedTaskExecutor, loop2.taskExecutor.asUnownedTaskExecutor()) + } + } + } + + let task = Task(executorPreference: loop1.taskExecutor) { + loop1.assertInEventLoop() + loop2.assertNotInEventLoop() + + withUnsafeCurrentTask { task in + // this currently fails on macOS + XCTAssertEqual(task?.unownedTaskExecutor, loop1.taskExecutor.asUnownedTaskExecutor()) + } + } + + await task.value + } + #endif + + @available(macOS 9999.0, iOS 9999.0, watchOS 9999.0, tvOS 9999.0, *) + func testSelectableEventLoopAsTaskExecutor() async throws { #if compiler(>=6.0) let group = MultiThreadedEventLoopGroup(numberOfThreads: 2) defer { try! group.syncShutdownGracefully() } - let loops = Array(group.makeIterator()) + var iterator = group.makeIterator() + let loop1 = iterator.next()! + let loop2 = iterator.next()! + + await self._runTests(loop1: loop1, loop2: loop2) + #endif + } + + @available(macOS 9999.0, iOS 9999.0, watchOS 9999.0, tvOS 9999.0, *) + func testAsyncTestingEventLoopAsTaskExecutor() async throws { + #if compiler(>=6.0) + let loop1 = NIOAsyncTestingEventLoop() + let loop2 = NIOAsyncTestingEventLoop() + defer { + try? loop1.syncShutdownGracefully() + try? loop2.syncShutdownGracefully() + } + await withTaskGroup(of: Void.self) { taskGroup in - taskGroup.addTask(executorPreference: loops[0].taskExecutor) { - loops[0].assertInEventLoop() - loops[1].assertNotInEventLoop() + taskGroup.addTask(executorPreference: loop1.taskExecutor) { + loop1.assertInEventLoop() + loop2.assertNotInEventLoop() withUnsafeCurrentTask { task in // this currently fails on macOS - XCTAssertEqual(task?.unownedTaskExecutor, loops[0].taskExecutor.asUnownedTaskExecutor()) + XCTAssertEqual(task?.unownedTaskExecutor, loop1.taskExecutor.asUnownedTaskExecutor()) } } - taskGroup.addTask(executorPreference: loops[1].taskExecutor) { - loops[0].assertNotInEventLoop() - loops[1].assertInEventLoop() + taskGroup.addTask(executorPreference: loop2) { + loop1.assertNotInEventLoop() + loop2.assertInEventLoop() withUnsafeCurrentTask { task in // this currently fails on macOS - XCTAssertEqual(task?.unownedTaskExecutor, loops[1].taskExecutor.asUnownedTaskExecutor()) + XCTAssertEqual(task?.unownedTaskExecutor, loop2.taskExecutor.asUnownedTaskExecutor()) } } } From 0ffa27a3cc301e0bc016736536b2fbe88a8b68c0 Mon Sep 17 00:00:00 2001 From: Fabian Fett Date: Tue, 4 Jun 2024 11:38:48 +0200 Subject: [PATCH 3/5] Make Benchmarks better --- .../NIOPosixBenchmarks/Benchmarks.swift | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/Benchmarks/Benchmarks/NIOPosixBenchmarks/Benchmarks.swift b/Benchmarks/Benchmarks/NIOPosixBenchmarks/Benchmarks.swift index 825fb9e576..53af20dbca 100644 --- a/Benchmarks/Benchmarks/NIOPosixBenchmarks/Benchmarks.swift +++ b/Benchmarks/Benchmarks/NIOPosixBenchmarks/Benchmarks.swift @@ -20,11 +20,12 @@ private let eventLoop = MultiThreadedEventLoopGroup.singleton.next() let benchmarks = { let defaultMetrics: [BenchmarkMetric] = [ .mallocCountTotal, - .cpuTotal + .cpuTotal, + .contextSwitches ] Benchmark( - "TCPEcho", + "TCPEcho pure NIO 1M times", configuration: .init( metrics: defaultMetrics, scalingFactor: .one @@ -36,6 +37,19 @@ let benchmarks = { ) } + Benchmark( + "TCPEcho pure async/await NIO 1M times", + configuration: .init( + metrics: defaultMetrics, + scalingFactor: .one + ) + ) { benchmark in + try await runTCPEchoAsyncChannel( + numberOfWrites: 1_000_000, + eventLoop: eventLoop + ) + } + // This benchmark is only available above 5.9 since our EL conformance // to serial executor is also gated behind 5.9. #if compiler(>=5.9) @@ -67,7 +81,7 @@ let benchmarks = { #if compiler(>=6.0) if #available(macOS 15.0, *) { Benchmark( - "TCPEchoAsyncChannel using task executor preference", + "TCPEchoAsyncChannel using task executor preference 1M times", configuration: .init( metrics: defaultMetrics, scalingFactor: .one From 10b11f6c1685062bc0fe5b674aeff44d8d58e46d Mon Sep 17 00:00:00 2001 From: Fabian Fett Date: Tue, 4 Jun 2024 13:22:00 +0200 Subject: [PATCH 4/5] Fix name --- Benchmarks/Benchmarks/NIOPosixBenchmarks/Benchmarks.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Benchmarks/Benchmarks/NIOPosixBenchmarks/Benchmarks.swift b/Benchmarks/Benchmarks/NIOPosixBenchmarks/Benchmarks.swift index 53af20dbca..e340941bdf 100644 --- a/Benchmarks/Benchmarks/NIOPosixBenchmarks/Benchmarks.swift +++ b/Benchmarks/Benchmarks/NIOPosixBenchmarks/Benchmarks.swift @@ -38,7 +38,7 @@ let benchmarks = { } Benchmark( - "TCPEcho pure async/await NIO 1M times", + "TCPEchoAsyncChannel pure async/await 1M times", configuration: .init( metrics: defaultMetrics, scalingFactor: .one From 8ec67a199716b3f1dd37e04af032e7b9e8e4adbd Mon Sep 17 00:00:00 2001 From: Fabian Fett Date: Tue, 11 Jun 2024 12:48:03 +0200 Subject: [PATCH 5/5] Update availability checks --- Sources/NIOCore/EventLoop+SerialExecutor.swift | 6 +++--- Sources/NIOCore/EventLoop.swift | 2 +- Sources/NIOEmbedded/AsyncTestingEventLoop.swift | 2 +- Sources/NIOEmbedded/Embedded.swift | 2 +- Sources/NIOPosix/SelectableEventLoop.swift | 2 +- Tests/NIOPosixTests/TaskExecutorTests.swift | 6 +++--- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Sources/NIOCore/EventLoop+SerialExecutor.swift b/Sources/NIOCore/EventLoop+SerialExecutor.swift index 6453452bde..865ce769cd 100644 --- a/Sources/NIOCore/EventLoop+SerialExecutor.swift +++ b/Sources/NIOCore/EventLoop+SerialExecutor.swift @@ -86,10 +86,10 @@ extension NIODefaultEventLoopExecutor: SerialExecutor { /// /// Implementers of `EventLoop` should consider conforming to this protocol as /// well on Swift 6.0 and later. -@available(macOS 9999.0, iOS 9999.0, watchOS 9999.0, tvOS 9999.0, *) +@available(macOS 15.0, iOS 18.0, tvOS 18.0, watchOS 11.0, *) public protocol NIOTaskEventLoopExecutor: NIOSerialEventLoopExecutor & TaskExecutor { } -@available(macOS 9999.0, iOS 9999.0, watchOS 9999.0, tvOS 9999.0, *) +@available(macOS 15.0, iOS 18.0, tvOS 18.0, watchOS 11.0, *) extension NIOTaskEventLoopExecutor { @inlinable func asUnownedTaskExecutor() -> UnownedTaskExecutor { @@ -102,7 +102,7 @@ extension NIOTaskEventLoopExecutor { } } -@available(macOS 9999.0, iOS 9999.0, watchOS 9999.0, tvOS 9999.0, *) +@available(macOS 15.0, iOS 18.0, tvOS 18.0, watchOS 11.0, *) extension NIODefaultEventLoopExecutor: TaskExecutor { @inlinable public func asUnownedTaskExecutor() -> UnownedTaskExecutor { diff --git a/Sources/NIOCore/EventLoop.swift b/Sources/NIOCore/EventLoop.swift index 45dfa8ff52..62ad3cf071 100644 --- a/Sources/NIOCore/EventLoop.swift +++ b/Sources/NIOCore/EventLoop.swift @@ -400,7 +400,7 @@ extension EventLoop { #endif #if compiler(>=6.0) - @available(macOS 9999.0, iOS 9999.0, watchOS 9999.0, tvOS 9999.0, *) + @available(macOS 15.0, iOS 9999.0, watchOS 9999.0, tvOS 9999.0, *) public var taskExecutor: any TaskExecutor { NIODefaultEventLoopExecutor(self) } diff --git a/Sources/NIOEmbedded/AsyncTestingEventLoop.swift b/Sources/NIOEmbedded/AsyncTestingEventLoop.swift index 58f20b6bd0..8bca6bbfa6 100644 --- a/Sources/NIOEmbedded/AsyncTestingEventLoop.swift +++ b/Sources/NIOEmbedded/AsyncTestingEventLoop.swift @@ -357,7 +357,7 @@ extension NIOAsyncTestingEventLoop: NIOSerialEventLoopExecutor { } // MARK: TaskExecutor conformance #if compiler(>=6.0) -@available(macOS 9999.0, iOS 9999.0, watchOS 9999.0, tvOS 9999.0, *) +@available(macOS 15.0, iOS 18.0, tvOS 18.0, watchOS 11.0, *) extension NIOAsyncTestingEventLoop: NIOTaskEventLoopExecutor { } #endif diff --git a/Sources/NIOEmbedded/Embedded.swift b/Sources/NIOEmbedded/Embedded.swift index 9e2eb23cdf..fee4d60d43 100644 --- a/Sources/NIOEmbedded/Embedded.swift +++ b/Sources/NIOEmbedded/Embedded.swift @@ -242,7 +242,7 @@ public final class EmbeddedEventLoop: EventLoop { #endif #if compiler(>=6.0) - @available(macOS 9999.0, iOS 9999.0, watchOS 9999.0, tvOS 9999.0, *) + @available(macOS 15.0, iOS 18.0, tvOS 18.0, watchOS 11.0, *) public var taskExecutor: any TaskExecutor { fatalError("EmbeddedEventLoop is not thread safe and cannot be used as a TaskExecutor. Use NIOAsyncTestingEventLoop instead.") } diff --git a/Sources/NIOPosix/SelectableEventLoop.swift b/Sources/NIOPosix/SelectableEventLoop.swift index da32500213..2da4e2a816 100644 --- a/Sources/NIOPosix/SelectableEventLoop.swift +++ b/Sources/NIOPosix/SelectableEventLoop.swift @@ -886,6 +886,6 @@ internal func assertExpression(_ body: () -> Bool) { // MARK: TaskExecutor conformance #if compiler(>=6.0) -@available(macOS 9999.0, iOS 9999.0, watchOS 9999.0, tvOS 9999.0, *) +@available(macOS 15.0, iOS 18.0, tvOS 18.0, watchOS 11.0, *) extension SelectableEventLoop: NIOTaskEventLoopExecutor { } #endif diff --git a/Tests/NIOPosixTests/TaskExecutorTests.swift b/Tests/NIOPosixTests/TaskExecutorTests.swift index bd8cf4bc84..a245ef4c5f 100644 --- a/Tests/NIOPosixTests/TaskExecutorTests.swift +++ b/Tests/NIOPosixTests/TaskExecutorTests.swift @@ -19,7 +19,7 @@ import XCTest final class TaskExecutorTests: XCTestCase { #if compiler(>=6.0) - @available(macOS 9999.0, iOS 9999.0, watchOS 9999.0, tvOS 9999.0, *) + @available(macOS 15.0, iOS 18.0, tvOS 18.0, watchOS 11.0, *) func _runTests(loop1: some EventLoop, loop2: some EventLoop) async { await withTaskGroup(of: Void.self) { taskGroup in taskGroup.addTask(executorPreference: loop1.taskExecutor) { @@ -57,7 +57,7 @@ final class TaskExecutorTests: XCTestCase { } #endif - @available(macOS 9999.0, iOS 9999.0, watchOS 9999.0, tvOS 9999.0, *) + @available(macOS 15.0, iOS 18.0, tvOS 18.0, watchOS 11.0, *) func testSelectableEventLoopAsTaskExecutor() async throws { #if compiler(>=6.0) let group = MultiThreadedEventLoopGroup(numberOfThreads: 2) @@ -72,7 +72,7 @@ final class TaskExecutorTests: XCTestCase { #endif } - @available(macOS 9999.0, iOS 9999.0, watchOS 9999.0, tvOS 9999.0, *) + @available(macOS 15.0, iOS 18.0, tvOS 18.0, watchOS 11.0, *) func testAsyncTestingEventLoopAsTaskExecutor() async throws { #if compiler(>=6.0) let loop1 = NIOAsyncTestingEventLoop()