Bootstrap: test that returning futures from foreign ELs are okay (#55)
Motivation: NIO on BSD Sockets had an issue where it wouldn't tolerate futures from foreign EventLoops being returned from the channel initializers. NIOTS should also have a test that tests this situation despite the fact that it didn't have the same bug. Modification: - add a test case - add more assertions Result: Better tests.
This commit is contained in:
parent
217f948d9b
commit
720645045c
|
|
@ -173,7 +173,8 @@ public final class NIOTSConnectionBootstrap {
|
|||
return channelOptions.applyAllChannelOptions(to: conn).flatMap {
|
||||
initializer(conn)
|
||||
}.flatMap {
|
||||
conn.register()
|
||||
conn.eventLoop.assertInEventLoop()
|
||||
return conn.register()
|
||||
}.flatMap {
|
||||
let connectPromise: EventLoopPromise<Void> = conn.eventLoop.makePromise()
|
||||
connectAction(conn, connectPromise)
|
||||
|
|
|
|||
|
|
@ -263,8 +263,9 @@ public final class NIOTSListenerBootstrap {
|
|||
return serverChannelOptions.applyAllChannelOptions(to: serverChannel).flatMap {
|
||||
serverChannelInit(serverChannel)
|
||||
}.flatMap {
|
||||
serverChannel.pipeline.addHandler(AcceptHandler(childChannelInitializer: childChannelInit,
|
||||
childChannelOptions: childChannelOptions))
|
||||
eventLoop.assertInEventLoop()
|
||||
return serverChannel.pipeline.addHandler(AcceptHandler(childChannelInitializer: childChannelInit,
|
||||
childChannelOptions: childChannelOptions))
|
||||
}.flatMap {
|
||||
serverChannel.register()
|
||||
}.flatMap {
|
||||
|
|
@ -348,7 +349,7 @@ private class AcceptHandler: ChannelInboundHandler {
|
|||
} else {
|
||||
fireThroughPipeline(childLoop.submit {
|
||||
return setupChildChannel()
|
||||
}.flatMap { $0 }.hop(to: ctxEventLoop))
|
||||
}.flatMap { $0 }.hop(to: ctxEventLoop))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,109 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This source file is part of the SwiftNIO open source project
|
||||
//
|
||||
// Copyright (c) 2019 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
|
||||
// swift-tools-version:4.0
|
||||
//
|
||||
// swift-tools-version:4.0
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#if canImport(Network)
|
||||
import XCTest
|
||||
import Network
|
||||
import NIO
|
||||
import NIOTransportServices
|
||||
import NIOConcurrencyHelpers
|
||||
import Foundation
|
||||
|
||||
@available(OSX 10.14, iOS 12.0, tvOS 12.0, *)
|
||||
final class NIOTSBootstrapTests: XCTestCase {
|
||||
var groupBag: [NIOTSEventLoopGroup]? = nil // protected by `self.lock`
|
||||
let lock = Lock()
|
||||
|
||||
override func setUp() {
|
||||
self.lock.withLock {
|
||||
XCTAssertNil(self.groupBag)
|
||||
self.groupBag = []
|
||||
}
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
XCTAssertNoThrow(try self.lock.withLock {
|
||||
guard let groupBag = self.groupBag else {
|
||||
XCTFail()
|
||||
return
|
||||
}
|
||||
XCTAssertNoThrow(try groupBag.forEach {
|
||||
XCTAssertNoThrow(try $0.syncShutdownGracefully())
|
||||
})
|
||||
self.groupBag = nil
|
||||
})
|
||||
}
|
||||
|
||||
func freshEventLoop() -> EventLoop {
|
||||
let group: NIOTSEventLoopGroup = .init(loopCount: 1, defaultQoS: .default)
|
||||
self.lock.withLock {
|
||||
self.groupBag!.append(group)
|
||||
}
|
||||
return group.next()
|
||||
}
|
||||
|
||||
func testBootstrapsTolerateFuturesFromDifferentEventLoopsReturnedInInitializers() throws {
|
||||
let childChannelDone = self.freshEventLoop().makePromise(of: Void.self)
|
||||
let serverChannelDone = self.freshEventLoop().makePromise(of: Void.self)
|
||||
let serverChannel = try assertNoThrowWithValue(NIOTSListenerBootstrap(group: self.freshEventLoop())
|
||||
.childChannelInitializer { channel in
|
||||
channel.eventLoop.preconditionInEventLoop()
|
||||
defer {
|
||||
childChannelDone.succeed(())
|
||||
}
|
||||
return self.freshEventLoop().makeSucceededFuture(())
|
||||
}
|
||||
.serverChannelInitializer { channel in
|
||||
channel.eventLoop.preconditionInEventLoop()
|
||||
defer {
|
||||
serverChannelDone.succeed(())
|
||||
}
|
||||
return self.freshEventLoop().makeSucceededFuture(())
|
||||
}
|
||||
.bind(host: "127.0.0.1", port: 0)
|
||||
.wait())
|
||||
defer {
|
||||
XCTAssertNoThrow(try serverChannel.close().wait())
|
||||
}
|
||||
|
||||
let client = try assertNoThrowWithValue(NIOTSConnectionBootstrap(group: self.freshEventLoop())
|
||||
.channelInitializer { channel in
|
||||
channel.eventLoop.preconditionInEventLoop()
|
||||
return self.freshEventLoop().makeSucceededFuture(())
|
||||
}
|
||||
.connect(to: serverChannel.localAddress!)
|
||||
.wait())
|
||||
defer {
|
||||
XCTAssertNoThrow(try client.syncCloseAcceptingAlreadyClosed())
|
||||
}
|
||||
XCTAssertNoThrow(try childChannelDone.futureResult.wait())
|
||||
XCTAssertNoThrow(try serverChannelDone.futureResult.wait())
|
||||
}
|
||||
}
|
||||
|
||||
extension Channel {
|
||||
func syncCloseAcceptingAlreadyClosed() throws {
|
||||
do {
|
||||
try self.close().wait()
|
||||
} catch ChannelError.alreadyClosed {
|
||||
/* we're happy with this one */
|
||||
} catch let e {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
Loading…
Reference in New Issue