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

Code before:
    ) throws -> OneWriteOperationResult {
        assert(
            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

Review comment (suggestion):
This code path leaks the promise.

Generally speaking, at this point we've taken ownership of the promise. It's our responsibility to do something with it, which we must do on all code paths.

Code after:
            "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)
    }

    /// 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)