Skip to content

NIOAsyncChannel read AsyncSequence cancellation can leave channel in a hung state (no close + pending reads never resume) #3472

@Montana

Description

@Montana

Hey all,

When consuming a NIOAsyncChannel's inbound AsyncSequence using for await, cancelling the reading task while a read is in flight can leave the channel in a hung state:

  • the channel remains active (no close event)
  • pending reads are never resumed
  • no error is surfaced
  • the connection must be force-closed externally

This appears to be a missing cancellation or cleanup path in the async read pipeline.


Minimal reproduction

The example below isolates the behavior by cancelling a task that is iterating the inbound stream:

import NIOCore
import NIOPosix
// import NIOAsyncChannel as appropriate

func reproduce(with asyncChannel: NIOAsyncChannel<ByteBuffer, ByteBuffer>) async throws {
    let reader = Task {
        do {
            for try await _ in asyncChannel.inboundStream {
                // simulate partial consumption
                break
            }
        } catch {
            print("reader error:", error)
        }
    }

    // allow a read to begin
    try await Task.sleep(nanoseconds: 100_000_000)

    // cancel while a read may be inflight
    reader.cancel()

    try await Task.sleep(nanoseconds: 500_000_000)

    print("channel isActive =", asyncChannel.channel.isActive)
}

This can be reproduced using a simple client/server pair (for example with ServerBootstrap and ClientBootstrap) and invoking reproduce(with:) on the server-side NIOAsyncChannel.

Michael.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions