.NET: Peibekwe/workflows cancellation token fix (#1740)

* Propagate cancellation token down the stack

* Added unit tests to cover workflow cancellation scenarios

* Updated tests based on feedback to simplify assert.

* Create custom AsyncEnumrable to gracefully handle cancellation for Channel reader. Tailor cancellation tests to declarative scenarios.

* Update comment and naming for readability.

* Fixing minor stylistic recommendation.

---------

Co-authored-by: Chris <66376200+crickman@users.noreply.github.com>
This commit is contained in:
Peter Ibekwe
2025-11-03 16:34:55 +00:00
committed by GitHub
co-authored by Chris
parent e87eed573b
commit c83011b30d
3 changed files with 121 additions and 11 deletions
@@ -139,11 +139,9 @@ internal sealed class StreamingRunEventStream : IRunEventStream
// Get the current epoch - we'll only respond to completion signals from this epoch or later
int myEpoch = Volatile.Read(ref this._completionEpoch) + 1;
// Simply read from channel - all coordination is handled by Channel infrastructure
// Note: When cancellation is requested, ReadAllAsync may throw OperationCanceledException
// or may complete the enumeration. We check IsCancellationRequested explicitly at superstep
// boundaries to ensure clean cancellation.
await foreach (WorkflowEvent evt in this._eventChannel.Reader.ReadAllAsync(cancellationToken).ConfigureAwait(false))
// Use custom async enumerable to avoid exceptions on cancellation.
NonThrowingChannelReaderAsyncEnumerable<WorkflowEvent> eventStream = new(this._eventChannel.Reader);
await foreach (WorkflowEvent evt in eventStream.WithCancellation(cancellationToken).ConfigureAwait(false))
{
// Filter out internal signals used for run loop coordination
if (evt is InternalHaltSignal completionSignal)