fix: RunState is sometimes Running when it should be Idle

There is a timeout-wait for messages in the inner loop driving the running of the workflow. When it gets cleared, an attempt to run a superstep is made. Currently we set the state to running as soon as the wait clears, even if we have no work to do, and clear it upon discovering this.

The fix is to only set the Running state when actual Workflow execution is happening when running Super-Steps, and not until the loop confirms there is work to do.

A broader-term fix would be to remove the Semaphore and timeout-wait in it, since it is working around the inability to atomicaly insert and release the Semaphore,
This commit is contained in:
Jacob Alber
2025-12-04 18:42:24 -05:00
Unverified
parent 40d3a0655c
commit 5b5960a546
@@ -69,7 +69,6 @@ internal sealed class StreamingRunEventStream : IRunEventStream
// The consumer will call EnqueueMessageAsync which signals the run loop
await this._inputWaiter.WaitForInputAsync(cancellationToken: linkedSource.Token).ConfigureAwait(false);
this._runStatus = RunStatus.Running;
activity?.AddEvent(new ActivityEvent(EventNames.WorkflowStarted));
while (!linkedSource.Token.IsCancellationRequested)
@@ -78,6 +77,9 @@ internal sealed class StreamingRunEventStream : IRunEventStream
// Events are streamed out in real-time as they happen via the event handler
while (this._stepRunner.HasUnprocessedMessages && !linkedSource.Token.IsCancellationRequested)
{
this._runStatus = RunStatus.Running; // Ensure we do not inappropriately signal Running status unless
// messages are being processed.
await this._stepRunner.RunSuperStepAsync(linkedSource.Token).ConfigureAwait(false);
}
@@ -96,9 +98,6 @@ internal sealed class StreamingRunEventStream : IRunEventStream
// Wait for next input from the consumer
// Works for both Idle (no work) and PendingRequests (waiting for responses)
await this._inputWaiter.WaitForInputAsync(TimeSpan.FromSeconds(1), linkedSource.Token).ConfigureAwait(false);
// When signaled, resume running
this._runStatus = RunStatus.Running;
}
}
catch (OperationCanceledException)