Skip to content

Unbounded ProcessInParallel extension abandons started tasks fire-and-forget on mid-enumeration failure #364

Description

@thomhurst

Found in the pre-4.0 audit (follow-up to #360/#361).

AsyncEnumerableExtensions.ProcessInParallel<T, TOutput>(items, selector, maxConcurrency: null, ...) — the unbounded path has no try/finally around enumeration, unlike the equivalent processor class. If MoveNextAsync throws or the token cancels mid-enumeration, tasks already started keep running fire-and-forget and their failures surface only as UnobservedTaskException at GC.

Failure scenario: source yields 50 items then throws; 50 selector tasks are in flight → the extension rethrows the enumeration error immediately, the 50 tasks are abandoned, and any of their exceptions hit TaskScheduler.UnobservedTaskException later.

Same fluent shape via the processor class (SelectAsync(...).ProcessInParallel()) produces a structured wait in its finally; the extension and processor paths should behave identically.

Proposal: wrap enumeration in try/finally that awaits (and observes) started tasks, mirroring AsyncEnumerableParallelProcessor — while coordinating with the exception-masking fix tracked separately.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions