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:
parent
f554552edf
commit
b73fc4e90d
|
@ -19,12 +19,13 @@ public enum ByteToMessageDecoderVerifier {
|
||||||
///
|
///
|
||||||
/// Verify `ByteToMessageDecoder`s with `String` inputs
|
/// Verify `ByteToMessageDecoder`s with `String` inputs
|
||||||
public static func verifyDecoder<Decoder: ByteToMessageDecoder>(stringInputOutputPairs: [(String, [Decoder.InboundOut])],
|
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 alloc = ByteBufferAllocator()
|
||||||
let ioPairs = stringInputOutputPairs.map { (ioPair: (String, [Decoder.InboundOut])) -> (ByteBuffer, [Decoder.InboundOut]) in
|
let ioPairs = stringInputOutputPairs.map { (ioPair: (String, [Decoder.InboundOut])) -> (ByteBuffer, [Decoder.InboundOut]) in
|
||||||
return (alloc.buffer(string: ioPair.0), ioPair.1)
|
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.
|
/// Verifies a `ByteToMessageDecoder` by performing a number of tests.
|
||||||
|
@ -51,7 +52,7 @@ public enum ByteToMessageDecoderVerifier {
|
||||||
/// XCTAssertNoThrow(try ByteToMessageDecoderVerifier.verifyDecoder(inputOutputPairs: expectedInOuts,
|
/// XCTAssertNoThrow(try ByteToMessageDecoderVerifier.verifyDecoder(inputOutputPairs: expectedInOuts,
|
||||||
/// decoderFactory: { ExampleDecoder() }))
|
/// decoderFactory: { ExampleDecoder() }))
|
||||||
public static func verifyDecoder<Decoder: ByteToMessageDecoder>(inputOutputPairs: [(ByteBuffer, [Decoder.InboundOut])],
|
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
|
typealias Out = Decoder.InboundOut
|
||||||
|
|
||||||
func verifySimple(channel: RecordingChannel) throws {
|
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
|
||||||
|
|
|
@ -23,7 +23,7 @@ import NIOConcurrencyHelpers
|
||||||
///
|
///
|
||||||
/// - note: Contrary to most `ChannelHandler`s, all of `EventCounterHandler`'s API is thread-safe meaning that you can
|
/// - 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.
|
/// 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 _channelRegisteredCalls = NIOAtomic<Int>.makeAtomic(value: 0)
|
||||||
private let _channelUnregisteredCalls = NIOAtomic<Int>.makeAtomic(value: 0)
|
private let _channelUnregisteredCalls = NIOAtomic<Int>.makeAtomic(value: 0)
|
||||||
private let _channelActiveCalls = NIOAtomic<Int>.makeAtomic(value: 0)
|
private let _channelActiveCalls = NIOAtomic<Int>.makeAtomic(value: 0)
|
||||||
|
|
|
@ -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 {
|
private final class WebServerHandler: ChannelDuplexHandler {
|
||||||
typealias InboundIn = HTTPServerRequestPart
|
typealias InboundIn = HTTPServerRequestPart
|
||||||
typealias OutboundIn = HTTPServerResponsePart
|
typealias OutboundIn = HTTPServerResponsePart
|
||||||
|
@ -303,6 +308,11 @@ extension NIOHTTP1TestServer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if swift(>=5.6)
|
||||||
|
@available(*, unavailable)
|
||||||
|
extension NIOHTTP1TestServer: Sendable {}
|
||||||
|
#endif
|
||||||
|
|
||||||
// MARK: - API for HTTP server
|
// MARK: - API for HTTP server
|
||||||
extension NIOHTTP1TestServer {
|
extension NIOHTTP1TestServer {
|
||||||
fileprivate func pushChannelRead(_ state: HTTPServerRequestPart) {
|
fileprivate func pushChannelRead(_ state: HTTPServerRequestPart) {
|
||||||
|
|
Loading…
Reference in New Issue