Repository: apple/swift-nio
File: Sources/NIOPosix/PendingWritesManager.swift (Swift)

Code before:
            self.state.isFlushPending && !self.state.isEmpty && self.isOpen,
            "vector write called in illegal state: flush pending: \(self.state.isFlushPending), empty: \(self.state.isEmpty), isOpen: \(self.isOpen)"
        )
        let result = try doPendingWriteVectorOperation(
            pending: self.state,
            bufferPool: bufferPool,
            { try operation($0) }
        )
        return self.didWrite(itemCount: result.itemCount, result: result.writeResult)
    }

    /// Signal that the `Channel` is closed.
    ///
    /// - Parameters:
    ///   - promise: Optionally an `EventLoopPromise` that will be succeeded once all outstanding writes have been dealt with
    func close(_ promise: EventLoopPromise<Void>?) -> CloseState {
        assert(self.isOpen)

        if self.isEmpty {
            switch self.outboundCloseState {
            case .open:
                self.outboundCloseState = .readyForClose(promise)
            case .readyForClose:
                ()
            case .pending, .closed:
                preconditionFailure("close called on channel in unexpected state: \(self.outboundCloseState)")
            }
        } else {
            self.outboundCloseState = .pending(promise)
        }
        return self.outboundCloseState
    }

    /// Fail all the outstanding writes.
    func failAll(error: Error, close: Bool) {
        if close {
            assert(self.isOpen)
            self.isOpen = false
            self.outboundCloseState = .closed
        }

        self.state.removeAll()?.fail(error)

        assert(self.state.isEmpty)
    }

    /// Initialize with a pre-allocated array of IO vectors and storage references. We pass in these pre-allocated
    /// objects to save allocations. They can be safely be re-used for all `Channel`s on a given `EventLoop` as an
    /// `EventLoop` always runs on one and the same thread. That means that there can't be any writes of more than
    /// one `Channel` on the same `EventLoop` at the same time.
    ///

Review comment (suggestion):
This is a touch nerve wracking. I can't see any reason this is actually impossible: I think if the user called `close(mode: .output)` twice in a row without the close having happened, they'd trap here. The same is true for the `closed` state.

More broadly, we have to tolerate the user doing this more than once. It's an error if they do, but that's ok.

Code after:
        let result = try doPendingWriteVectorOperation(
            pending: self.state,
            bufferPool: bufferPool,
            { try operation($0) }
        )
        return self.didWrite(itemCount: result.itemCount, result: result.writeResult)
    }

    /// Fail all the outstanding writes.
    func failAll(error: Error) -> EventLoopPromise<Void>? {
        assert(self.isOpen)

        let promise: EventLoopPromise<Void>?
        self.isOpen = false
        switch self.outboundCloseState {
        case .open, .closed:
            self.outboundCloseState = .closed
            promise = nil
        case .pending(let closePromise), .readyForClose(let closePromise):
            self.outboundCloseState = .closed
            promise = closePromise
        }

        self.state.removeAll()?.fail(error)

        assert(self.state.isEmpty)
        return promise
    }

    // The result of calling `closeOutbound`
    enum CloseOutboundResult {
        case pending
        case readyForClose(EventLoopPromise<Void>?)
        case closed(EventLoopPromise<Void>?)
        case errored(Error, EventLoopPromise<Void>?)

        init(_ closeState: CloseState, _ isEmpty: Bool, _ promise: EventLoopPromise<Void>?) {
            switch closeState {
            case .open:
                assertionFailure(
                    "We are in .open state after being asked to close. This should never happen."
                )
                self = .errored(NIOReportedOpenAfterClose(), promise)
            case .pending:
                // `promise` has already been taken care of in the pending state for later completion
                self = .pending
            case .readyForClose(let closePromise):
                if isEmpty {
                    self = .readyForClose(closePromise)
                } else {
                    assertionFailure(