Skip to content

Pre-cancelled token surfaces internal ArgumentException instead of canceled tasks #367

Description

@thomhurst

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

ProcessorLifecycle's constructor calls ValidationHelper.ValidateCancellationTokenSource, which throws ArgumentException("'cancellationTokenSource' has already been cancelled.") when the linked source is already cancelled. The parameter it names is an internal constructor parameter the user never passed.

Failure scenario: items.ForEachAsync(f, alreadyCancelledToken).ProcessInParallel() throws ArgumentException from the terminal builder call. TPL convention for a pre-cancelled token is a canceled task, not an argument error.

Inconsistency — three behaviors for the same input:

  • Builder path (enumerable source): ArgumentException at the terminal call
  • ParallelExtensions.InParallelAsync: returns a canceled task
  • Async-enumerable streaming path: no validation; OperationCanceledException when ExecuteAsync is awaited

Race: a token cancelling between builder creation and the terminal call makes the observed exception type nondeterministic. No test covers any of this.

Proposal: drop the already-cancelled throw (keep the null check); let Start's registration fire immediately so every per-item completion source is canceled and WaitAsync/GetResultsAsync observe cancellation. The registration-in-Start design already makes this safe on a fully built instance. Contract change — decide before 4.0.

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