Skip to content

Let a Stop() during a pending RestartAsync() win [patch] - #63

Merged
matt-edmondson merged 1 commit into
mainfrom
claude/intervalaction-57-stop-during-restart
Sep 28, 2026
Merged

matt-edmondson merged 1 commit into
mainfrom
claude/intervalaction-57-stop-during-restart

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #57

Problem

RestartCoreAsync set ShouldPoll = true once its awaits finished, whatever had happened while it waited. A Stop() that arrived while a restart was still waiting on the old loop was overwritten, and the action kept running. The repro is RestartAsync(); Stop(); await r;.

Change

All in IntervalAction/IntervalAction.cs:

  • Generation counter: Stop() now increments a StopGeneration counter under Lock.
  • Restart checks it: RestartAsync() records the counter when the restart is requested. After its awaits, the restart only re-enables polling and starts a loop if the counter hasn't changed; otherwise it ends without starting one.
  • No self-cancel: the restart's own "stop the old loop" step now clears ShouldPoll directly instead of calling Stop(), so the restart doesn't cancel itself.
  • Later restarts still work: a restart requested after a Stop() resumes polling as before.

Tests

  • Added StopDuringAPendingRestartKeepsTheActionStopped. It stops while the restart is waiting on the old loop's delay, then checks that the loop ends, that the action doesn't run over the next 500 ms, and that a later RestartAsync() still resumes polling. The wait on the loop is bounded so the test fails rather than hangs if the bug comes back.
  • With the source change reverted, the new test fails: the loop never ends.
  • With the fix, the full suite passes 19 of 19, on three consecutive runs.

Related

#61 (Stop() then Restart() leaving an orphaned loop) is already fixed on main and covered by RestartRightAfterStopLeavesOnlyOnePollingLoop, so this PR doesn't touch it.

🤖 Generated with Claude Code

https://claude.ai/code/session_01P1dENrzXtWG4vP7thSjyof


Generated by Claude Code

RestartCoreAsync set ShouldPoll = true after its awaits regardless of
what happened meanwhile, so a Stop() that arrived while a restart was
waiting for the old loop was overwritten and the action kept running.
Stop() now bumps a generation counter; a restart records it when it is
requested and ends without starting a loop if a Stop() has happened
since. The restart's own internal stop no longer goes through Stop(),
so it does not cancel itself.

Fixes #57

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P1dENrzXtWG4vP7thSjyof
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Stop() called while a RestartAsync() is still pending is ignored, so the action keeps running after the caller stopped it

2 participants