diff --git a/Benchmarks/Thresholds/nightly-next/NIOPosixBenchmarks.TCPEcho.p90.json b/Benchmarks/Thresholds/nightly-next/NIOPosixBenchmarks.TCPEcho.p90.json index c6a93680d0a..e1c0a531662 100644 --- a/Benchmarks/Thresholds/nightly-next/NIOPosixBenchmarks.TCPEcho.p90.json +++ b/Benchmarks/Thresholds/nightly-next/NIOPosixBenchmarks.TCPEcho.p90.json @@ -1,3 +1,3 @@ { - "mallocCountTotal" : 108 + "mallocCountTotal": 108 } diff --git a/Benchmarks/Thresholds/nightly-next/NIOPosixBenchmarks.TCPEchoAsyncChannel.p90.json b/Benchmarks/Thresholds/nightly-next/NIOPosixBenchmarks.TCPEchoAsyncChannel.p90.json index 390ed2415a7..d714da6722b 100644 --- a/Benchmarks/Thresholds/nightly-next/NIOPosixBenchmarks.TCPEchoAsyncChannel.p90.json +++ b/Benchmarks/Thresholds/nightly-next/NIOPosixBenchmarks.TCPEchoAsyncChannel.p90.json @@ -1,3 +1,3 @@ { - "mallocCountTotal" : 164376 -} \ No newline at end of file + "mallocCountTotal": 82500 +} diff --git a/IntegrationTests/tests_04_performance/Thresholds/nightly-main.json b/IntegrationTests/tests_04_performance/Thresholds/nightly-main.json index a5ce735741c..968f9a35669 100644 --- a/IntegrationTests/tests_04_performance/Thresholds/nightly-main.json +++ b/IntegrationTests/tests_04_performance/Thresholds/nightly-main.json @@ -1,17 +1,16 @@ { - "10000000_asyncsequenceproducer": 19, - "1000000_asyncwriter": 1000050, - "1000_addHandlers": 44050, + "1_reqs_1000_conn": 384050, "1000_addHandlers_sync": 37050, + "1000_addHandlers": 44050, "1000_addRemoveHandlers_handlercontext": 8050, "1000_addRemoveHandlers_handlername": 8050, "1000_addRemoveHandlers_handlertype": 8050, - "1000_autoReadGetAndSet": 18050, "1000_autoReadGetAndSet_sync": 0, + "1000_autoReadGetAndSet": 18050, "1000_copying_bytebufferview_to_array": 1050, "1000_copying_circularbuffer_to_array": 1050, - "1000_getHandlers": 8050, "1000_getHandlers_sync": 35, + "1000_getHandlers": 8050, "1000_reqs_1_conn": 26400, "1000_rst_connections": 145050, "1000_tcpbootstraps": 3050, @@ -19,29 +18,30 @@ "1000_udp_reqs": 6050, "1000_udpbootstraps": 2050, "1000_udpconnections": 75050, - "1_reqs_1000_conn": 384050, + "1000000_asyncwriter": 1000050, + "10000000_asyncsequenceproducer": 20, "assume_isolated_scheduling_10000_executions": 89, "bytebuffer_lots_of_rw": 2050, "creating_10000_headers": 0, "decode_1000_ws_frames": 2050, - "encode_1000_ws_frames_holding_buffer": 3, "encode_1000_ws_frames_holding_buffer_with_mask": 2050, - "encode_1000_ws_frames_holding_buffer_with_space": 3, "encode_1000_ws_frames_holding_buffer_with_space_with_mask": 2050, - "encode_1000_ws_frames_new_buffer": 3050, + "encode_1000_ws_frames_holding_buffer_with_space": 3, + "encode_1000_ws_frames_holding_buffer": 3, "encode_1000_ws_frames_new_buffer_with_mask": 5050, - "encode_1000_ws_frames_new_buffer_with_space": 3050, "encode_1000_ws_frames_new_buffer_with_space_with_mask": 5050, + "encode_1000_ws_frames_new_buffer_with_space": 3050, + "encode_1000_ws_frames_new_buffer": 3050, "execute_hop_10000_tasks": 0, "flat_schedule_10000_tasks": 100100, "flat_schedule_assume_isolated_10000_tasks": 80100, "future_assume_isolated_lots_of_callbacks": 74050, "future_erase_result": 4050, "future_lots_of_callbacks": 74050, - "get_100000_headers_canonical_form": 500050, - "get_100000_headers_canonical_form_trimming_whitespace": 500050, "get_100000_headers_canonical_form_trimming_whitespace_from_long_string": 500050, "get_100000_headers_canonical_form_trimming_whitespace_from_short_string": 500050, + "get_100000_headers_canonical_form_trimming_whitespace": 500050, + "get_100000_headers_canonical_form": 500050, "modifying_1000_circular_buffer_elements": 0, "modifying_byte_buffer_view": 6050, "ping_pong_1000_reqs_1_conn": 314, @@ -54,6 +54,6 @@ "scheduling_10000_executions": 89, "submit_10000_tasks": 20100, "submit_assume_isolated_10000_tasks": 20100, - "udp_1000_reqs_1_conn": 6200, - "udp_1_reqs_1000_conn": 162050 + "udp_1_reqs_1000_conn": 162050, + "udp_1000_reqs_1_conn": 6200 } diff --git a/IntegrationTests/tests_04_performance/Thresholds/nightly-next.json b/IntegrationTests/tests_04_performance/Thresholds/nightly-next.json index 29a7bf1e7c4..31ab97ea2c4 100644 --- a/IntegrationTests/tests_04_performance/Thresholds/nightly-next.json +++ b/IntegrationTests/tests_04_performance/Thresholds/nightly-next.json @@ -1,17 +1,16 @@ { - "10000000_asyncsequenceproducer": 19, - "1000000_asyncwriter": 1000050, - "1000_addHandlers": 44050, + "1_reqs_1000_conn": 384000, "1000_addHandlers_sync": 37050, + "1000_addHandlers": 44050, "1000_addRemoveHandlers_handlercontext": 8050, "1000_addRemoveHandlers_handlername": 8050, "1000_addRemoveHandlers_handlertype": 8050, - "1000_autoReadGetAndSet": 18050, "1000_autoReadGetAndSet_sync": 0, + "1000_autoReadGetAndSet": 18050, "1000_copying_bytebufferview_to_array": 1050, "1000_copying_circularbuffer_to_array": 1050, - "1000_getHandlers": 8050, "1000_getHandlers_sync": 35, + "1000_getHandlers": 8050, "1000_reqs_1_conn": 26400, "1000_rst_connections": 145050, "1000_tcpbootstraps": 3050, @@ -19,29 +18,30 @@ "1000_udp_reqs": 6050, "1000_udpbootstraps": 2050, "1000_udpconnections": 75050, - "1_reqs_1000_conn": 384050, + "1000000_asyncwriter": 1000050, + "10000000_asyncsequenceproducer": 20, "assume_isolated_scheduling_10000_executions": 89, "bytebuffer_lots_of_rw": 2050, "creating_10000_headers": 0, "decode_1000_ws_frames": 2050, - "encode_1000_ws_frames_holding_buffer": 3, "encode_1000_ws_frames_holding_buffer_with_mask": 2050, - "encode_1000_ws_frames_holding_buffer_with_space": 3, "encode_1000_ws_frames_holding_buffer_with_space_with_mask": 2050, - "encode_1000_ws_frames_new_buffer": 3050, + "encode_1000_ws_frames_holding_buffer_with_space": 3, + "encode_1000_ws_frames_holding_buffer": 3, "encode_1000_ws_frames_new_buffer_with_mask": 5050, - "encode_1000_ws_frames_new_buffer_with_space": 3050, "encode_1000_ws_frames_new_buffer_with_space_with_mask": 5050, + "encode_1000_ws_frames_new_buffer_with_space": 3050, + "encode_1000_ws_frames_new_buffer": 3050, "execute_hop_10000_tasks": 0, "flat_schedule_10000_tasks": 100100, - "flat_schedule_assume_isolated_10000_tasks": 90100, + "flat_schedule_assume_isolated_10000_tasks": 80100, "future_assume_isolated_lots_of_callbacks": 74050, "future_erase_result": 4050, "future_lots_of_callbacks": 74050, - "get_100000_headers_canonical_form": 700050, - "get_100000_headers_canonical_form_trimming_whitespace": 700050, - "get_100000_headers_canonical_form_trimming_whitespace_from_long_string": 700050, - "get_100000_headers_canonical_form_trimming_whitespace_from_short_string": 700050, + "get_100000_headers_canonical_form_trimming_whitespace_from_long_string": 500050, + "get_100000_headers_canonical_form_trimming_whitespace_from_short_string": 500050, + "get_100000_headers_canonical_form_trimming_whitespace": 500050, + "get_100000_headers_canonical_form": 500050, "modifying_1000_circular_buffer_elements": 0, "modifying_byte_buffer_view": 6050, "ping_pong_1000_reqs_1_conn": 314, @@ -54,6 +54,6 @@ "scheduling_10000_executions": 89, "submit_10000_tasks": 20100, "submit_assume_isolated_10000_tasks": 20100, - "udp_1000_reqs_1_conn": 6200, - "udp_1_reqs_1000_conn": 162050 -} + "udp_1_reqs_1000_conn": 162050, + "udp_1000_reqs_1_conn": 6200 +} \ No newline at end of file diff --git a/Sources/NIOCore/AsyncAwaitSupport.swift b/Sources/NIOCore/AsyncAwaitSupport.swift index ba8dfa3cf6e..bd49ac1b67f 100644 --- a/Sources/NIOCore/AsyncAwaitSupport.swift +++ b/Sources/NIOCore/AsyncAwaitSupport.swift @@ -280,7 +280,10 @@ extension ChannelPipeline { "ChannelHandlerContext is not Sendable and it is therefore not safe to be used outside of its EventLoop" ) @inlinable - public func context(handlerType: Handler.Type) async throws -> ChannelHandlerContext { + @preconcurrency + public func context( + handlerType: Handler.Type + ) async throws -> ChannelHandlerContext { try await self.context(handlerType: handlerType).map { UnsafeTransfer($0) }.get().wrappedValue } diff --git a/Sources/NIOCore/ChannelPipeline.swift b/Sources/NIOCore/ChannelPipeline.swift index 6b63c5e2bb6..d53f7eb00b0 100644 --- a/Sources/NIOCore/ChannelPipeline.swift +++ b/Sources/NIOCore/ChannelPipeline.swift @@ -534,7 +534,10 @@ public final class ChannelPipeline: ChannelInvoker { /// - handlerType: The type of the handler to search for. /// - Returns: the `EventLoopFuture` which will be notified once the the operation completes. @inlinable - public func context(handlerType: Handler.Type) -> EventLoopFuture { + @preconcurrency + public func context( + handlerType: Handler.Type + ) -> EventLoopFuture { let promise = self.eventLoop.makePromise(of: ChannelHandlerContext.self) if self.eventLoop.inEventLoop { @@ -555,7 +558,10 @@ public final class ChannelPipeline: ChannelInvoker { /// - Returns: An ``EventLoopFuture`` that is succeeded if a handler of the given type is contained in the pipeline. Otherwise /// the future will be failed with an error. @inlinable - public func containsHandler(type: Handler.Type) -> EventLoopFuture { + @preconcurrency + public func containsHandler( + type: Handler.Type + ) -> EventLoopFuture { self.handler(type: type).map { _ in () } } @@ -2424,7 +2430,10 @@ extension ChannelPipeline: CustomDebugStringConvertible { /// - Parameters: /// - type: the type of `ChannelHandler` to return. @inlinable - public func handler(type _: Handler.Type) -> EventLoopFuture { + @preconcurrency + public func handler( + type _: Handler.Type + ) -> EventLoopFuture { self.context(handlerType: Handler.self).map { context in guard let typedContext = context.handler as? Handler else { preconditionFailure( diff --git a/Sources/NIOCore/NIOCoreSendableMetatype.swift b/Sources/NIOCore/NIOCoreSendableMetatype.swift new file mode 100644 index 00000000000..10bd769c7ca --- /dev/null +++ b/Sources/NIOCore/NIOCoreSendableMetatype.swift @@ -0,0 +1,19 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftNIO open source project +// +// Copyright (c) 2025 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 +// +//===----------------------------------------------------------------------===// + +#if compiler(>=6.2) +public typealias _NIOCoreSendableMetatype = SendableMetatype +#else +public typealias _NIOCoreSendableMetatype = Any +#endif diff --git a/Sources/NIOFileSystem/DirectoryEntries.swift b/Sources/NIOFileSystem/DirectoryEntries.swift index 1c0fa9d0b71..409d7362f86 100644 --- a/Sources/NIOFileSystem/DirectoryEntries.swift +++ b/Sources/NIOFileSystem/DirectoryEntries.swift @@ -36,7 +36,8 @@ public struct DirectoryEntries: AsyncSequence, Sendable { /// Creates a ``DirectoryEntries`` sequence by wrapping an `AsyncSequence` of _batches_ of /// directory entries. @preconcurrency - public init(wrapping sequence: S) where S.Element == Batched.Element { + public init(wrapping sequence: S) + where S.Element == Batched.Element, S.AsyncIterator: _NIOFileSystemSendableMetatype { self.batchedSequence = Batched(wrapping: sequence) } @@ -95,7 +96,8 @@ extension DirectoryEntries { /// Creates a ``DirectoryEntries/Batched`` sequence by wrapping an `AsyncSequence` /// of directory entry batches. @preconcurrency - public init(wrapping sequence: S) where S.Element == Element { + public init(wrapping sequence: S) + where S.Element == Element, S.AsyncIterator: _NIOFileSystemSendableMetatype { self.stream = BufferedOrAnyStream<[DirectoryEntry], DirectoryEntryProducer>(wrapping: sequence) } diff --git a/Sources/NIOFileSystem/FileChunks.swift b/Sources/NIOFileSystem/FileChunks.swift index 00338236b05..50f09486e73 100644 --- a/Sources/NIOFileSystem/FileChunks.swift +++ b/Sources/NIOFileSystem/FileChunks.swift @@ -31,7 +31,9 @@ public struct FileChunks: AsyncSequence, Sendable { private let stream: BufferedOrAnyStream /// Create a ``FileChunks`` sequence backed by wrapping an `AsyncSequence`. - public init(wrapping sequence: S) where S.Element == ByteBuffer { + @preconcurrency + public init(wrapping sequence: S) + where S.Element == ByteBuffer, S.AsyncIterator: _NIOFileSystemSendableMetatype { self.stream = BufferedOrAnyStream(wrapping: sequence) } diff --git a/Sources/NIOFileSystem/Internal/BufferedOrAnyStream.swift b/Sources/NIOFileSystem/Internal/BufferedOrAnyStream.swift index 50e097b931e..7e307a16023 100644 --- a/Sources/NIOFileSystem/Internal/BufferedOrAnyStream.swift +++ b/Sources/NIOFileSystem/Internal/BufferedOrAnyStream.swift @@ -28,7 +28,8 @@ internal enum BufferedOrAnyStream(wrapping stream: S) where S.Element == Element { + internal init(wrapping stream: S) + where S.Element == Element, S.AsyncIterator: _NIOFileSystemSendableMetatype { self = .anyAsyncSequence(AnyAsyncSequence(wrapping: stream)) } @@ -72,7 +73,8 @@ internal enum BufferedOrAnyStream: AsyncSequence, Sendable { private let _makeAsyncIterator: @Sendable () -> AsyncIterator - internal init(wrapping sequence: S) where S.Element == Element { + internal init(wrapping sequence: S) + where S.Element == Element, S.AsyncIterator: _NIOFileSystemSendableMetatype { self._makeAsyncIterator = { AsyncIterator(wrapping: sequence.makeAsyncIterator()) } @@ -82,7 +84,7 @@ internal struct AnyAsyncSequence: AsyncSequence, Sendable { self._makeAsyncIterator() } - internal struct AsyncIterator: AsyncIteratorProtocol { + internal struct AsyncIterator: AsyncIteratorProtocol, _NIOFileSystemSendableMetatype { private var iterator: any AsyncIteratorProtocol init(wrapping iterator: I) where I.Element == Element { diff --git a/Sources/NIOFileSystem/NIOFileSystemSendableMetatype.swift b/Sources/NIOFileSystem/NIOFileSystemSendableMetatype.swift new file mode 100644 index 00000000000..226cb70cdce --- /dev/null +++ b/Sources/NIOFileSystem/NIOFileSystemSendableMetatype.swift @@ -0,0 +1,19 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftNIO open source project +// +// Copyright (c) 2025 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 +// +//===----------------------------------------------------------------------===// + +#if compiler(>=6.2) +public typealias _NIOFileSystemSendableMetatype = SendableMetatype +#else +public typealias _NIOFileSystemSendableMetatype = Any +#endif diff --git a/Sources/NIOPosix/NIOPosixSendableMetatype.swift b/Sources/NIOPosix/NIOPosixSendableMetatype.swift new file mode 100644 index 00000000000..184932d44cc --- /dev/null +++ b/Sources/NIOPosix/NIOPosixSendableMetatype.swift @@ -0,0 +1,19 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftNIO open source project +// +// Copyright (c) 2025 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 +// +//===----------------------------------------------------------------------===// + +#if compiler(>=6.2) +public typealias _NIOPosixSendableMetatype = SendableMetatype +#else +public typealias _NIOPosixSendableMetatype = Any +#endif diff --git a/Sources/NIOPosix/SocketProtocols.swift b/Sources/NIOPosix/SocketProtocols.swift index b376a02be1b..7f3feba1e87 100644 --- a/Sources/NIOPosix/SocketProtocols.swift +++ b/Sources/NIOPosix/SocketProtocols.swift @@ -13,7 +13,7 @@ //===----------------------------------------------------------------------===// import NIOCore -protocol BaseSocketProtocol: CustomStringConvertible { +protocol BaseSocketProtocol: CustomStringConvertible, _NIOPosixSendableMetatype { associatedtype SelectableType: Selectable var isOpen: Bool { get } diff --git a/Sources/NIOWebSocket/NIOWebSocketClientUpgrader.swift b/Sources/NIOWebSocket/NIOWebSocketClientUpgrader.swift index d3aba88b4c5..65a21db4886 100644 --- a/Sources/NIOWebSocket/NIOWebSocketClientUpgrader.swift +++ b/Sources/NIOWebSocket/NIOWebSocketClientUpgrader.swift @@ -129,9 +129,6 @@ public final class NIOTypedWebSocketClientUpgrader: NIO } } -@available(*, unavailable) -extension NIOWebSocketClientUpgrader: Sendable {} - extension NIOWebSocketClientUpgrader { /// Generates a random WebSocket Request Key by generating 16 bytes randomly and encoding them as a base64 string as defined in RFC6455 https://tools.ietf.org/html/rfc6455#section-4.1 /// - Parameter generator: the `RandomNumberGenerator` used as a the source of randomness diff --git a/Tests/NIOHTTP1Tests/HTTPServerUpgradeTests.swift b/Tests/NIOHTTP1Tests/HTTPServerUpgradeTests.swift index bc8d58ab348..4e4c84e9d05 100644 --- a/Tests/NIOHTTP1Tests/HTTPServerUpgradeTests.swift +++ b/Tests/NIOHTTP1Tests/HTTPServerUpgradeTests.swift @@ -25,7 +25,7 @@ extension ChannelPipeline { try self.assertDoesNotContain(handlerType: HTTPServerUpgradeHandler.self) } - func assertDoesNotContain( + func assertDoesNotContain( handlerType: Handler.Type, file: StaticString = #filePath, line: UInt = #line @@ -49,7 +49,7 @@ extension ChannelPipeline { } } - func assertContains(handlerType: Handler.Type) { + func assertContains(handlerType: Handler.Type) { XCTAssertNoThrow(try self.containsHandler(type: handlerType).wait(), "did not find handler") }