From 4042c06b987ef3b8888631eec3b17c47072b435b Mon Sep 17 00:00:00 2001 From: Fabian Fett Date: Wed, 29 May 2024 14:35:02 +0200 Subject: [PATCH 1/9] Add TaskExecutor conformance to EventLoops --- .../NIOCore/EventLoop+SerialExecutor.swift | 44 +++++++++++++--- Sources/NIOCore/EventLoop.swift | 9 +++- Sources/NIOPosix/SelectableEventLoop.swift | 8 +++ Tests/NIOPosixTests/TaskExecutorTests.swift | 51 +++++++++++++++++++ 4 files changed, 104 insertions(+), 8 deletions(-) create mode 100644 Tests/NIOPosixTests/TaskExecutorTests.swift diff --git a/Sources/NIOCore/EventLoop+SerialExecutor.swift b/Sources/NIOCore/EventLoop+SerialExecutor.swift index d8486a59a1..864df535ac 100644 --- a/Sources/NIOCore/EventLoop+SerialExecutor.swift +++ b/Sources/NIOCore/EventLoop+SerialExecutor.swift @@ -62,31 +62,61 @@ 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 { +package final class NIODefaultEventLoopExecutor { @usableFromInline let loop: EventLoop @inlinable - init(_ loop: EventLoop) { + package init(_ loop: EventLoop) { self.loop = loop } } @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) { + package func enqueue(_ job: consuming ExecutorJob) { self.loop.enqueue(job) } @inlinable - public func asUnownedSerialExecutor() -> UnownedSerialExecutor { + package func asUnownedSerialExecutor() -> UnownedSerialExecutor { UnownedSerialExecutor(complexEquality: self) - } @inlinable - public func isSameExclusiveExecutionContext(other: NIODefaultSerialEventLoopExecutor) -> Bool { + package func isSameExclusiveExecutionContext(other: NIODefaultEventLoopExecutor) -> Bool { self.loop === other.loop } } + +#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 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) +public protocol NIOTaskEventLoopExecutor: NIOSerialEventLoopExecutor & TaskExecutor { } + +@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) +extension NIOTaskEventLoopExecutor { + @inlinable + func asUnownedTaskExecutor() -> UnownedTaskExecutor { + UnownedTaskExecutor(ordinary: self) + } + + @inlinable + public var taskExecutor: any TaskExecutor { + self + } +} + +@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.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 fddfb32e71..0ab9a27d48 100644 --- a/Sources/NIOCore/EventLoop.swift +++ b/Sources/NIOCore/EventLoop.swift @@ -538,7 +538,7 @@ extension EventLoop { extension EventLoop { @available(macOS 14.0, iOS 17.0, watchOS 10.0, tvOS 17.0, *) public var executor: any SerialExecutor { - NIODefaultSerialEventLoopExecutor(self) + NIODefaultEventLoopExecutor(self) } @inlinable @@ -552,6 +552,13 @@ extension EventLoop { unownedJob.runSynchronously(on: self.executor.asUnownedSerialExecutor()) } } + + #if compiler(>=6.0) + @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) + public var taskExecutor: any TaskExecutor { + NIODefaultEventLoopExecutor(self) + } + #endif } extension EventLoopGroup { diff --git a/Sources/NIOPosix/SelectableEventLoop.swift b/Sources/NIOPosix/SelectableEventLoop.swift index bfad156e72..69ede5fd38 100644 --- a/Sources/NIOPosix/SelectableEventLoop.swift +++ b/Sources/NIOPosix/SelectableEventLoop.swift @@ -1080,3 +1080,11 @@ extension SelectableEventLoop { } } } + +// MARK: TaskExecutor conformance +#if compiler(>=6.0) +@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) +extension SelectableEventLoop: NIOTaskEventLoopExecutor { + +} +#endif diff --git a/Tests/NIOPosixTests/TaskExecutorTests.swift b/Tests/NIOPosixTests/TaskExecutorTests.swift new file mode 100644 index 0000000000..af22501e45 --- /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 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) + func testBasicExecutorFitsOnEventLoop_MTELG() async throws { + #if compiler(>=6.0) + let group = MultiThreadedEventLoopGroup(numberOfThreads: 2) + let loops = Array(group.makeIterator()) + await withTaskGroup(of: Void.self) { taskGroup in + let loop0Executor = loops[0].taskExecutor + let loop1Executor = loops[1].taskExecutor + taskGroup.addTask(executorPreference: loop0Executor) { + loops[0].assertInEventLoop() + loops[1].assertNotInEventLoop() + + withUnsafeCurrentTask { task in + // this currently fails on macOS + XCTAssertEqual(task?.unownedTaskExecutor, loop0Executor.asUnownedTaskExecutor()) + } + } + + taskGroup.addTask(executorPreference: loop1Executor) { + loops[0].assertNotInEventLoop() + loops[1].assertInEventLoop() + + withUnsafeCurrentTask { task in + // this currently fails on macOS + XCTAssertEqual(task?.unownedTaskExecutor, loop1Executor.asUnownedTaskExecutor()) + } + } + } + try await group.shutdownGracefully() + #endif + } +} From c151504e0b5777e60d364b5d5fc8657190eaf9ed Mon Sep 17 00:00:00 2001 From: Fabian Fett Date: Wed, 29 May 2024 19:56:54 +0200 Subject: [PATCH 2/9] Code review --- .../NIOPosixBenchmarks/Benchmarks.swift | 43 +++++++++++- .../NIOEmbedded/AsyncTestingEventLoop.swift | 6 ++ Sources/NIOEmbedded/Embedded.swift | 7 ++ Tests/NIOPosixTests/TaskExecutorTests.swift | 65 ++++++++++++++++++- 4 files changed, 116 insertions(+), 5 deletions(-) diff --git a/Benchmarks/Benchmarks/NIOPosixBenchmarks/Benchmarks.swift b/Benchmarks/Benchmarks/NIOPosixBenchmarks/Benchmarks.swift index 3cc33b5492..e59c4d3191 100644 --- a/Benchmarks/Benchmarks/NIOPosixBenchmarks/Benchmarks.swift +++ b/Benchmarks/Benchmarks/NIOPosixBenchmarks/Benchmarks.swift @@ -20,20 +20,29 @@ private let eventLoop = MultiThreadedEventLoopGroup.singleton.next() let benchmarks = { let defaultMetrics: [BenchmarkMetric] = [ +<<<<<<< HEAD .mallocCountTotal +======= + .mallocCountTotal, + .cpuTotal +>>>>>>> 31f491c4 (Code review) ] Benchmark( "TCPEcho", configuration: .init( metrics: defaultMetrics, +<<<<<<< HEAD scalingFactor: .mega, maxDuration: .seconds(10_000_000), maxIterations: 5 +======= + scalingFactor: .one +>>>>>>> 31f491c4 (Code review) ) ) { benchmark in try runTCPEcho( - numberOfWrites: benchmark.scaledIterations.upperBound, + numberOfWrites: 1_000_000, eventLoop: eventLoop ) } @@ -42,12 +51,16 @@ 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, +<<<<<<< HEAD scalingFactor: .mega, maxDuration: .seconds(10_000_000), maxIterations: 5, +======= + scalingFactor: .one, +>>>>>>> 31f491c4 (Code review) // 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])], @@ -62,12 +75,13 @@ let benchmarks = { ) ) { benchmark in try await runTCPEchoAsyncChannel( - numberOfWrites: benchmark.scaledIterations.upperBound, + numberOfWrites: 1_000_000, eventLoop: eventLoop ) } #endif +<<<<<<< HEAD Benchmark( "MTELG.scheduleTask(in:_:)", configuration: Benchmark.Configuration( @@ -101,4 +115,27 @@ let benchmarks = { let handle = try! eventLoop.scheduleCallback(in: .hours(1), handler: timer) } } +======= + #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 +>>>>>>> 31f491c4 (Code review) } diff --git a/Sources/NIOEmbedded/AsyncTestingEventLoop.swift b/Sources/NIOEmbedded/AsyncTestingEventLoop.swift index d7101cdb45..e9b09784f8 100644 --- a/Sources/NIOEmbedded/AsyncTestingEventLoop.swift +++ b/Sources/NIOEmbedded/AsyncTestingEventLoop.swift @@ -510,6 +510,12 @@ public final class NIOAsyncTestingEventLoop: EventLoop, @unchecked Sendable { @available(macOS 14.0, iOS 17.0, watchOS 10.0, tvOS 17.0, *) extension NIOAsyncTestingEventLoop: NIOSerialEventLoopExecutor {} +// 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 5959bc568e..5ccd9cb64b 100644 --- a/Sources/NIOEmbedded/Embedded.swift +++ b/Sources/NIOEmbedded/Embedded.swift @@ -408,6 +408,13 @@ public final class EmbeddedEventLoop: EventLoop, CustomStringConvertible { } return false }() + + #if compiler(>=6.0) + @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) + public var taskExecutor: any TaskExecutor { + fatalError("EmbeddedEventLoop is not thread safe and cannot be used as a TaskExecutor. Use NIOAsyncTestingEventLoop instead.") + } + #endif } // EmbeddedEventLoop is extremely _not_ Sendable. However, the EventLoop protocol diff --git a/Tests/NIOPosixTests/TaskExecutorTests.swift b/Tests/NIOPosixTests/TaskExecutorTests.swift index af22501e45..a0f237eeb4 100644 --- a/Tests/NIOPosixTests/TaskExecutorTests.swift +++ b/Tests/NIOPosixTests/TaskExecutorTests.swift @@ -17,9 +17,9 @@ import NIOPosix import XCTest final class TaskExecutorTests: XCTestCase { + #if compiler(>=6.0) @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) func testBasicExecutorFitsOnEventLoop_MTELG() async throws { - #if compiler(>=6.0) let group = MultiThreadedEventLoopGroup(numberOfThreads: 2) let loops = Array(group.makeIterator()) await withTaskGroup(of: Void.self) { taskGroup in @@ -46,6 +46,67 @@ final class TaskExecutorTests: XCTestCase { } } try await group.shutdownGracefully() - #endif } + + @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) + func _runTests(loop1: some EventLoop, loop2: some EventLoop) async { + let executor1 = loop1.taskExecutor + let executor2 = loop2.taskExecutor + await withTaskGroup(of: Void.self) { taskGroup in + taskGroup.addTask(executorPreference: executor1) { + loop1.assertInEventLoop() + loop2.assertNotInEventLoop() + + withUnsafeCurrentTask { task in + // this currently fails on macOS + XCTAssertEqual(task?.unownedTaskExecutor, executor1.asUnownedTaskExecutor()) + } + } + + taskGroup.addTask(executorPreference: executor2) { + loop1.assertNotInEventLoop() + loop2.assertInEventLoop() + + withUnsafeCurrentTask { task in + // this currently fails on macOS + XCTAssertEqual(task?.unownedTaskExecutor, executor2.asUnownedTaskExecutor()) + } + } + } + + let task = Task(executorPreference: executor1) { + loop1.assertInEventLoop() + loop2.assertNotInEventLoop() + + withUnsafeCurrentTask { task in + // this currently fails on macOS + XCTAssertEqual(task?.unownedTaskExecutor, executor1.asUnownedTaskExecutor()) + } + } + + await task.value + } + + @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) + func testSelectableEventLoopAsTaskExecutor() async throws { + let group = MultiThreadedEventLoopGroup(numberOfThreads: 2) + var iterator = group.makeIterator() + let loop1 = iterator.next()! + let loop2 = iterator.next()! + + await self._runTests(loop1: loop1, loop2: loop2) + try! await group.shutdownGracefully() + } + + @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) + func testAsyncTestingEventLoopAsTaskExecutor() async throws { + let loop1 = NIOAsyncTestingEventLoop() + let loop2 = NIOAsyncTestingEventLoop() + + await self._runTests(loop1: loop1, loop2: loop2) + + await loop1.shutdownGracefully() + await loop2.shutdownGracefully() + } + #endif } From 94c87d6931e1c058785f6607d68406c57ecba639 Mon Sep 17 00:00:00 2001 From: Fabian Fett Date: Tue, 4 Jun 2024 11:38:48 +0200 Subject: [PATCH 3/9] Make Benchmarks better --- .../NIOPosixBenchmarks/Benchmarks.swift | 38 +++++++++---------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/Benchmarks/Benchmarks/NIOPosixBenchmarks/Benchmarks.swift b/Benchmarks/Benchmarks/NIOPosixBenchmarks/Benchmarks.swift index e59c4d3191..5cddbca116 100644 --- a/Benchmarks/Benchmarks/NIOPosixBenchmarks/Benchmarks.swift +++ b/Benchmarks/Benchmarks/NIOPosixBenchmarks/Benchmarks.swift @@ -20,25 +20,16 @@ private let eventLoop = MultiThreadedEventLoopGroup.singleton.next() let benchmarks = { let defaultMetrics: [BenchmarkMetric] = [ -<<<<<<< HEAD - .mallocCountTotal -======= .mallocCountTotal, .cpuTotal ->>>>>>> 31f491c4 (Code review) + .contextSwitches ] Benchmark( - "TCPEcho", + "TCPEcho pure NIO 1M times", configuration: .init( metrics: defaultMetrics, -<<<<<<< HEAD - scalingFactor: .mega, - maxDuration: .seconds(10_000_000), - maxIterations: 5 -======= scalingFactor: .one ->>>>>>> 31f491c4 (Code review) ) ) { benchmark in try runTCPEcho( @@ -47,6 +38,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) @@ -54,13 +58,7 @@ let benchmarks = { "TCPEchoAsyncChannel using globalHook 1M times", configuration: .init( metrics: defaultMetrics, -<<<<<<< HEAD - scalingFactor: .mega, - maxDuration: .seconds(10_000_000), - maxIterations: 5, -======= scalingFactor: .one, ->>>>>>> 31f491c4 (Code review) // 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])], @@ -81,7 +79,6 @@ let benchmarks = { } #endif -<<<<<<< HEAD Benchmark( "MTELG.scheduleTask(in:_:)", configuration: Benchmark.Configuration( @@ -115,11 +112,11 @@ let benchmarks = { let handle = try! eventLoop.scheduleCallback(in: .hours(1), handler: timer) } } -======= + #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 @@ -137,5 +134,4 @@ let benchmarks = { } } #endif ->>>>>>> 31f491c4 (Code review) } From 3a3e84d6fd0b5831441f84629ad6a20def22c737 Mon Sep 17 00:00:00 2001 From: Fabian Fett Date: Tue, 4 Jun 2024 13:22:00 +0200 Subject: [PATCH 4/9] 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 5cddbca116..ddbbe3f311 100644 --- a/Benchmarks/Benchmarks/NIOPosixBenchmarks/Benchmarks.swift +++ b/Benchmarks/Benchmarks/NIOPosixBenchmarks/Benchmarks.swift @@ -39,7 +39,7 @@ let benchmarks = { } Benchmark( - "TCPEcho pure async/await NIO 1M times", + "TCPEchoAsyncChannel pure async/await 1M times", configuration: .init( metrics: defaultMetrics, scalingFactor: .one From 96e79f69a7a4cbfae95184c95670f1a760849376 Mon Sep 17 00:00:00 2001 From: Fabian Fett Date: Tue, 11 Jun 2024 12:48:03 +0200 Subject: [PATCH 5/9] Update availability checks --- .../NIOEmbedded/AsyncTestingEventLoop.swift | 2 +- Sources/NIOPosix/SelectableEventLoop.swift | 4 +-- Tests/NIOPosixTests/TaskExecutorTests.swift | 30 ------------------- 3 files changed, 2 insertions(+), 34 deletions(-) diff --git a/Sources/NIOEmbedded/AsyncTestingEventLoop.swift b/Sources/NIOEmbedded/AsyncTestingEventLoop.swift index e9b09784f8..1587fc4cb0 100644 --- a/Sources/NIOEmbedded/AsyncTestingEventLoop.swift +++ b/Sources/NIOEmbedded/AsyncTestingEventLoop.swift @@ -512,7 +512,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/NIOPosix/SelectableEventLoop.swift b/Sources/NIOPosix/SelectableEventLoop.swift index 69ede5fd38..3a17c08926 100644 --- a/Sources/NIOPosix/SelectableEventLoop.swift +++ b/Sources/NIOPosix/SelectableEventLoop.swift @@ -1084,7 +1084,5 @@ extension SelectableEventLoop { // MARK: TaskExecutor conformance #if compiler(>=6.0) @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) -extension SelectableEventLoop: NIOTaskEventLoopExecutor { - -} +extension SelectableEventLoop: NIOTaskEventLoopExecutor { } #endif diff --git a/Tests/NIOPosixTests/TaskExecutorTests.swift b/Tests/NIOPosixTests/TaskExecutorTests.swift index a0f237eeb4..3d9db3426e 100644 --- a/Tests/NIOPosixTests/TaskExecutorTests.swift +++ b/Tests/NIOPosixTests/TaskExecutorTests.swift @@ -18,36 +18,6 @@ import XCTest final class TaskExecutorTests: XCTestCase { #if compiler(>=6.0) - @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) - func testBasicExecutorFitsOnEventLoop_MTELG() async throws { - let group = MultiThreadedEventLoopGroup(numberOfThreads: 2) - let loops = Array(group.makeIterator()) - await withTaskGroup(of: Void.self) { taskGroup in - let loop0Executor = loops[0].taskExecutor - let loop1Executor = loops[1].taskExecutor - taskGroup.addTask(executorPreference: loop0Executor) { - loops[0].assertInEventLoop() - loops[1].assertNotInEventLoop() - - withUnsafeCurrentTask { task in - // this currently fails on macOS - XCTAssertEqual(task?.unownedTaskExecutor, loop0Executor.asUnownedTaskExecutor()) - } - } - - taskGroup.addTask(executorPreference: loop1Executor) { - loops[0].assertNotInEventLoop() - loops[1].assertInEventLoop() - - withUnsafeCurrentTask { task in - // this currently fails on macOS - XCTAssertEqual(task?.unownedTaskExecutor, loop1Executor.asUnownedTaskExecutor()) - } - } - } - try await group.shutdownGracefully() - } - @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) func _runTests(loop1: some EventLoop, loop2: some EventLoop) async { let executor1 = loop1.taskExecutor From 3cad1af34659d832352403ed45ca3b653e8a619c Mon Sep 17 00:00:00 2001 From: Fabian Fett Date: Thu, 3 Apr 2025 01:20:11 +0200 Subject: [PATCH 6/9] Update Benchmarks --- Benchmarks/Benchmarks/NIOPosixBenchmarks/Benchmarks.swift | 4 +--- Benchmarks/Package.swift | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Benchmarks/Benchmarks/NIOPosixBenchmarks/Benchmarks.swift b/Benchmarks/Benchmarks/NIOPosixBenchmarks/Benchmarks.swift index ddbbe3f311..bfc972188f 100644 --- a/Benchmarks/Benchmarks/NIOPosixBenchmarks/Benchmarks.swift +++ b/Benchmarks/Benchmarks/NIOPosixBenchmarks/Benchmarks.swift @@ -21,7 +21,7 @@ private let eventLoop = MultiThreadedEventLoopGroup.singleton.next() let benchmarks = { let defaultMetrics: [BenchmarkMetric] = [ .mallocCountTotal, - .cpuTotal + .cpuTotal, .contextSwitches ] @@ -53,7 +53,6 @@ let benchmarks = { // 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) Benchmark( "TCPEchoAsyncChannel using globalHook 1M times", configuration: .init( @@ -77,7 +76,6 @@ let benchmarks = { eventLoop: eventLoop ) } - #endif Benchmark( "MTELG.scheduleTask(in:_:)", diff --git a/Benchmarks/Package.swift b/Benchmarks/Package.swift index 13bef8d521..a6efa1d0ea 100644 --- a/Benchmarks/Package.swift +++ b/Benchmarks/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version: 5.7 +// swift-tools-version: 5.9 import PackageDescription From cb67cb2f8461b4ebc95729c78e4e016f1ff3ed0b Mon Sep 17 00:00:00 2001 From: Fabian Fett Date: Thu, 3 Apr 2025 01:38:02 +0200 Subject: [PATCH 7/9] Moved code around --- .../NIOPosixBenchmarks/Benchmarks.swift | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/Benchmarks/Benchmarks/NIOPosixBenchmarks/Benchmarks.swift b/Benchmarks/Benchmarks/NIOPosixBenchmarks/Benchmarks.swift index bfc972188f..36ddb909ef 100644 --- a/Benchmarks/Benchmarks/NIOPosixBenchmarks/Benchmarks.swift +++ b/Benchmarks/Benchmarks/NIOPosixBenchmarks/Benchmarks.swift @@ -77,6 +77,28 @@ let benchmarks = { ) } + #if compiler(>=6.0) + if #available(macOS 15.0, *) { + Benchmark( + "TCPEchoAsyncChannel using task executor preference 1M times", + 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 + Benchmark( "MTELG.scheduleTask(in:_:)", configuration: Benchmark.Configuration( @@ -110,26 +132,4 @@ let benchmarks = { let handle = try! eventLoop.scheduleCallback(in: .hours(1), handler: timer) } } - - #if compiler(>=6.0) - if #available(macOS 15.0, *) { - Benchmark( - "TCPEchoAsyncChannel using task executor preference 1M times", - 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 } From 5423d30a8faaed2d01e0942d4bc459cdda094dab Mon Sep 17 00:00:00 2001 From: Fabian Fett Date: Thu, 3 Apr 2025 11:11:50 +0200 Subject: [PATCH 8/9] swift-format --- Benchmarks/Benchmarks/NIOPosixBenchmarks/Benchmarks.swift | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Benchmarks/Benchmarks/NIOPosixBenchmarks/Benchmarks.swift b/Benchmarks/Benchmarks/NIOPosixBenchmarks/Benchmarks.swift index 36ddb909ef..93b310b531 100644 --- a/Benchmarks/Benchmarks/NIOPosixBenchmarks/Benchmarks.swift +++ b/Benchmarks/Benchmarks/NIOPosixBenchmarks/Benchmarks.swift @@ -22,7 +22,7 @@ let benchmarks = { let defaultMetrics: [BenchmarkMetric] = [ .mallocCountTotal, .cpuTotal, - .contextSwitches + .contextSwitches, ] Benchmark( @@ -84,9 +84,6 @@ let benchmarks = { 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) { From e5e22db38547bf5a82a976e1ea649d7edd53da31 Mon Sep 17 00:00:00 2001 From: Fabian Fett Date: Thu, 3 Apr 2025 11:17:18 +0200 Subject: [PATCH 9/9] Fix format --- Sources/NIOCore/EventLoop+SerialExecutor.swift | 2 +- Sources/NIOEmbedded/AsyncTestingEventLoop.swift | 2 +- Sources/NIOEmbedded/Embedded.swift | 4 +++- Sources/NIOPosix/SelectableEventLoop.swift | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Sources/NIOCore/EventLoop+SerialExecutor.swift b/Sources/NIOCore/EventLoop+SerialExecutor.swift index 864df535ac..756f244915 100644 --- a/Sources/NIOCore/EventLoop+SerialExecutor.swift +++ b/Sources/NIOCore/EventLoop+SerialExecutor.swift @@ -97,7 +97,7 @@ extension NIODefaultEventLoopExecutor: SerialExecutor { /// Implementers of `EventLoop` should consider conforming to this protocol as /// well on Swift 6.0 and later. @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) -public protocol NIOTaskEventLoopExecutor: NIOSerialEventLoopExecutor & TaskExecutor { } +public protocol NIOTaskEventLoopExecutor: NIOSerialEventLoopExecutor & TaskExecutor {} @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) extension NIOTaskEventLoopExecutor { diff --git a/Sources/NIOEmbedded/AsyncTestingEventLoop.swift b/Sources/NIOEmbedded/AsyncTestingEventLoop.swift index 1587fc4cb0..3bb2a5e893 100644 --- a/Sources/NIOEmbedded/AsyncTestingEventLoop.swift +++ b/Sources/NIOEmbedded/AsyncTestingEventLoop.swift @@ -513,7 +513,7 @@ extension NIOAsyncTestingEventLoop: NIOSerialEventLoopExecutor {} // MARK: TaskExecutor conformance #if compiler(>=6.0) @available(macOS 15.0, iOS 18.0, tvOS 18.0, watchOS 11.0, *) -extension NIOAsyncTestingEventLoop: NIOTaskEventLoopExecutor { } +extension NIOAsyncTestingEventLoop: NIOTaskEventLoopExecutor {} #endif /// This is a thread-safe promise creation store. diff --git a/Sources/NIOEmbedded/Embedded.swift b/Sources/NIOEmbedded/Embedded.swift index 5ccd9cb64b..5f30084b1f 100644 --- a/Sources/NIOEmbedded/Embedded.swift +++ b/Sources/NIOEmbedded/Embedded.swift @@ -412,7 +412,9 @@ public final class EmbeddedEventLoop: EventLoop, CustomStringConvertible { #if compiler(>=6.0) @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) public var taskExecutor: any TaskExecutor { - fatalError("EmbeddedEventLoop is not thread safe and cannot be used as a TaskExecutor. Use NIOAsyncTestingEventLoop instead.") + fatalError( + "EmbeddedEventLoop is not thread safe and cannot be used as a TaskExecutor. Use NIOAsyncTestingEventLoop instead." + ) } #endif } diff --git a/Sources/NIOPosix/SelectableEventLoop.swift b/Sources/NIOPosix/SelectableEventLoop.swift index 3a17c08926..0b925a0b63 100644 --- a/Sources/NIOPosix/SelectableEventLoop.swift +++ b/Sources/NIOPosix/SelectableEventLoop.swift @@ -1084,5 +1084,5 @@ extension SelectableEventLoop { // MARK: TaskExecutor conformance #if compiler(>=6.0) @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) -extension SelectableEventLoop: NIOTaskEventLoopExecutor { } +extension SelectableEventLoop: NIOTaskEventLoopExecutor {} #endif