Repository: apple/swift-nio
File: Tests/NIOTests/ChannelTests.swift (Swift)

Code before:
        // has written something and then on registration something is available to be read. However it should not
        // be read yet (and in this case never will be) as we haven't send channelActive yet (which will never happen)
        // in this example.
        _ = try sc.selectable.connect(to: bootstrap.localAddress!)
        try serverWriteHappenedPromise.futureResult.wait()
        try sc.pipeline.add(handler: ReadDoesNotHappen(hasRegisteredPromise: clientHasRegistered,
                                                       hasUnregisteredPromise: clientHasUnregistered)).then {
                // this will succeed and should not cause the socket to be read even though there'll be something
                // available to be read immediately
                sc.register()
            }.then {
                // this will fail as the socket is already pre-connected ;)
                sc.connect(to: bootstrap.localAddress!).map {
                    XCTFail("should've failed")
                }.mapIfError { error in
                    switch error {
                    case let e as IOError where e.errnoCode == EISCONN:
                        // OK
                        ()
                    default:
                        XCTFail("wrong error \(error)")
                    }
                }
            }.wait()
        try clientHasRegistered.futureResult.wait()
        try sc.close().wait()
        try clientHasUnregistered.futureResult.wait()
    }
}


Review comment (suggestion):
I think this is very slightly probabilistic, as the close can technically race with the return to the selector such that it occurs before the selector is next polled. I think the only way you can avoid this is to connect-and-read-from another channel, which kinda sucks, but may be necessary.

Code after:
            typealias InboundIn = Any
            typealias OutboundOut = ByteBuffer

            private let writeDonePromise: EventLoopPromise<()>

            init(writeDonePromise: EventLoopPromise<()>) {
                self.writeDonePromise = writeDonePromise
            }

            func channelActive(ctx: ChannelHandlerContext) {
                var buffer = ctx.channel.allocator.buffer(capacity: 4)
                buffer.write(string: "foo")
                ctx.writeAndFlush(NIOAny(buffer), promise: self.writeDonePromise)
            }
        }

        let serverWriteHappenedPromise: EventLoopPromise<()> = serverEL.next().newPromise()
        let clientHasRegistered: EventLoopPromise<()> = serverEL.next().newPromise()
        let clientHasUnregistered: EventLoopPromise<()> = serverEL.next().newPromise()
        let clientHasRead: EventLoopPromise<()> = serverEL.next().newPromise()

        let bootstrap = try ServerBootstrap(group: serverEL)
            .childChannelInitializer { channel in
                channel.pipeline.add(handler: WriteImmediatelyHandler(writeDonePromise: serverWriteHappenedPromise))
            }
            .bind(host: "127.0.0.1", port: 0).wait()

        // This is a bit ugly, we're trying to fabricate a situation that can happen in the real world which is that
        // a socket is readable straight after becoming registered & connected.
        // In here what we're doing is that we flip the order around and connect it first, make sure the server
        // has written something and then on registration something is available to be read. We then 'fake connect'
        // again which our special `Socket` subclass will let succeed.
        _ = try sc.selectable.connect(to: bootstrap.localAddress!)
        try serverWriteHappenedPromise.futureResult.wait()
        try sc.pipeline.add(handler: ReadDoesNotHappen(hasRegisteredPromise: clientHasRegistered,
                                                       hasUnregisteredPromise: clientHasUnregistered,
                                                       hasReadPromise: clientHasRead)).then {
                // this will succeed and should not cause the socket to be read even though there'll be something
                // available to be read immediately
                sc.register()
            }.then {
                // this would normally fail but our special Socket subclass will let it succeed.
                sc.connect(to: try! SocketAddress(ipAddress: "127.0.0.1", port: 123))
            }.wait()
        try clientHasRegistered.futureResult.wait()
        try clientHasRead.futureResult.wait()
        try sc.syncCloseAcceptingAlreadyClosed()
        try clientHasUnregistered.futureResult.wait()
    }
}