Adopt `Sendable` in `NIOTestUtils` (#2199)

* Adopting `Sendable` in `NIOTestUtils`

The parameter `decoderFactory` of the static methods `ByteToMessageDecoderVerifier.verifyDecoder` do not need to be `@escaping`. I have made them non-escaping as part of `Sendable` adoption because we would otherwise need to think about if they should be `@Sendable` too.

`VerificationError` is interesting, see the code comment for more information.

* Adopting `Sendable` in `NIOTestUtils`

The parameter `decoderFactory` of the static methods `ByteToMessageDecoderVerifier.verifyDecoder` do not need to be `@escaping`. I have made them non-escaping as part of `Sendable` adoption because we would otherwise need to think about if they should be `@Sendable` too.

`VerificationError` is interesting, see the code comment for more information.

* Clarify the reason `VerificationError` already conforms to `Sendable`
This commit is contained in:
David Nadoba 2022-06-20 14:23:14 +02:00 committed by GitHub
parent f554552edf
commit b73fc4e90d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 28 additions and 4 deletions

View File

@ -19,12 +19,13 @@ public enum ByteToMessageDecoderVerifier {
///
/// Verify `ByteToMessageDecoder`s with `String` inputs
public static func verifyDecoder<Decoder: ByteToMessageDecoder>(stringInputOutputPairs: [(String, [Decoder.InboundOut])],
decoderFactory: @escaping () -> Decoder) throws where Decoder.InboundOut: Equatable {
decoderFactory: () -> Decoder) throws where Decoder.InboundOut: Equatable {
let alloc = ByteBufferAllocator()
let ioPairs = stringInputOutputPairs.map { (ioPair: (String, [Decoder.InboundOut])) -> (ByteBuffer, [Decoder.InboundOut]) in
return (alloc.buffer(string: ioPair.0), ioPair.1)
}
return try ByteToMessageDecoderVerifier.verifyDecoder(inputOutputPairs: ioPairs, decoderFactory: decoderFactory)
try ByteToMessageDecoderVerifier.verifyDecoder(inputOutputPairs: ioPairs, decoderFactory: decoderFactory)
}
/// Verifies a `ByteToMessageDecoder` by performing a number of tests.
@ -51,7 +52,7 @@ public enum ByteToMessageDecoderVerifier {
/// XCTAssertNoThrow(try ByteToMessageDecoderVerifier.verifyDecoder(inputOutputPairs: expectedInOuts,
/// decoderFactory: { ExampleDecoder() }))
public static func verifyDecoder<Decoder: ByteToMessageDecoder>(inputOutputPairs: [(ByteBuffer, [Decoder.InboundOut])],
decoderFactory: @escaping () -> Decoder) throws where Decoder.InboundOut: Equatable {
decoderFactory: () -> Decoder) throws where Decoder.InboundOut: Equatable {
typealias Out = Decoder.InboundOut
func verifySimple(channel: RecordingChannel) throws {
@ -207,3 +208,16 @@ extension ByteToMessageDecoderVerifier {
}
}
}
#if swift(>=5.5) && canImport(_Concurrency)
/// `VerificationError` conforms to `Error` and therefore needs to conform to `Sendable` too.
/// `VerificationError` has a stored property `errorCode` of type `ErrorCode` which can store `NIOAny` which is not and can not be `Sendable`.
/// In addtion, `ErrorCode` can also store a user defined `OutputType` which is not required to be `Sendable` but we could require it to be `Sendable`.
/// We have two choices:
/// - we could lie and conform `ErrorCode` to `Sendable` with `@unchecked`
/// - do the same but for `VerificationError`
/// As `VerificationError` already conforms to `Sendable` (because it conforms to `Error` and `Error` inherits from `Sendable`)
/// it sound like the best option to just stick to the conformances we already have and **not** lie twice by making `VerificationError` conform to `Sendable` too.
/// Note that this still allows us to adopt `Sendable` for `ErrorCode` later if we change our opinion.
extension ByteToMessageDecoderVerifier.VerificationError: @unchecked Sendable {}
#endif

View File

@ -23,7 +23,7 @@ import NIOConcurrencyHelpers
///
/// - note: Contrary to most `ChannelHandler`s, all of `EventCounterHandler`'s API is thread-safe meaning that you can
/// query the events received from any thread.
public final class EventCounterHandler {
public final class EventCounterHandler: NIOSendable {
private let _channelRegisteredCalls = NIOAtomic<Int>.makeAtomic(value: 0)
private let _channelUnregisteredCalls = NIOAtomic<Int>.makeAtomic(value: 0)
private let _channelActiveCalls = NIOAtomic<Int>.makeAtomic(value: 0)

View File

@ -46,6 +46,11 @@ private final class BlockingQueue<Element> {
}
}
#if swift(>=5.5) && canImport(_Concurrency)
extension BlockingQueue: @unchecked Sendable where Element: Sendable {}
#endif
private final class WebServerHandler: ChannelDuplexHandler {
typealias InboundIn = HTTPServerRequestPart
typealias OutboundIn = HTTPServerResponsePart
@ -303,6 +308,11 @@ extension NIOHTTP1TestServer {
}
}
#if swift(>=5.6)
@available(*, unavailable)
extension NIOHTTP1TestServer: Sendable {}
#endif
// MARK: - API for HTTP server
extension NIOHTTP1TestServer {
fileprivate func pushChannelRead(_ state: HTTPServerRequestPart) {