Skip to content

fix(polling): drop stale in-flight check results on cancel/resubscribe - #148

Open
mem-5514-tahara wants to merge 3 commits into
OutdatedGuy:mainfrom
mem-5514-tahara:fix/subscription-version-guard
Open

fix(polling): drop stale in-flight check results on cancel/resubscribe#148
mem-5514-tahara wants to merge 3 commits into
OutdatedGuy:mainfrom
mem-5514-tahara:fix/subscription-version-guard

Conversation

@mem-5514-tahara

Copy link
Copy Markdown

Summary

Split out from #139 per review feedback, with _cancelGeneration renamed to _subscriptionVersion.

If a listener cancels its subscription while a connectivity check is still running, that check's result belongs to the old subscription. Without a guard:

  • it could still get emitted to a brand-new subscriber that started its own fresh check moments later, or
  • it could restart the polling timer even though nobody is listening anymore.

This adds a _subscriptionVersion counter, bumped only when a listener cancels. _maybeEmitStatusUpdate snapshots it before the check starts and skips both the emit and the next timer reschedule if it changed while the check was in-flight.

Test plan

  • dart analyze — no issues
  • dart format --set-exit-if-changed — clean
  • dart test — all tests pass, including two new regression tests:
    • cancelled in-flight check does not emit stale result to new subscriber
    • does not restart the timer if cancelled while a check is in-flight

If a listener cancels while a connectivity check is still running, that
check's result belonged to the old subscription. Without a guard, it
could still get emitted to a brand-new subscriber that started its own
fresh check moments later, or restart the polling timer even though
nobody is listening anymore.

Track a _subscriptionVersion counter, bumped only when a listener
cancels. _maybeEmitStatusUpdate snapshots it before the check starts and
skips the emit (and the next timer reschedule) if it changed while the
check was in-flight.

@OutdatedGuy OutdatedGuy left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Few changes

Comment thread lib/src/internet_connection.dart Outdated
Comment thread lib/src/internet_connection.dart Outdated
Comment thread lib/src/internet_connection.dart Outdated
mem-5514-tahara added a commit to mem-5514-tahara/internet_connection_checker_plus that referenced this pull request Aug 18, 2026
Removing _generation entirely (previous commit) turned out to be wrong:
unlike _cancelGeneration, this guard protects backoff-specific mutable
state (_currentBackoffDelay/_backoffNeedsReset), which only exists in
this branch. Without it, a setIntervalAndResetTimer call (or a cancel)
racing an in-flight failing check silently undoes the reset it just
applied — the next failure incorrectly grows the delay instead of
starting over at the fresh initial delay.

Confirmed via a reproduction: removing the guard makes the added
regression test fail (checkCount climbs from 3 to 5, and the delay
grows to 100ms instead of staying at the reset 50ms).

Renamed from _generation to _timerVersion with a plainer doc comment,
per review feedback. _cancelGeneration (renamed _subscriptionVersion)
remains split out as OutdatedGuy#148, since that one has no backoff dependency.
Move the hasListener check right after the await so isStale no longer
needs to recheck it, and drop comments already covered by the
_subscriptionVersion doc comment.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds subscription-version tracking to discard connectivity results from cancelled subscriptions.

Changes:

  • Guards status emissions against stale in-flight checks.
  • Adds cancellation and resubscription regression tests.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
lib/src/internet_connection.dart Adds stale-result detection during polling.
test/internet_connection_test.dart Tests cancellation during in-flight checks.
Suppressed comments (1)

lib/src/internet_connection.dart:304

  • The version is advanced only after awaiting trigger-stream cleanup. If that subscription has asynchronous cancellation, a replacement listener can attach and the old connectivity check can finish while the version is still unchanged, allowing the stale result to be emitted to the replacement subscriber. Invalidate the generation synchronously before the first await.
  Future<void> _handleStatusChangeCancel() async {
    await _triggerSubscription?.cancel();
    _triggerSubscription = null;
    _subscriptionVersion++;

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread lib/src/internet_connection.dart Outdated
Comment on lines +288 to +290
final isStale = _subscriptionVersion != previousSubVersion;

if (!isStale && _lastStatus != currentStatus) {

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — a version mismatch now returns immediately, skipping both emission and rescheduling.

A stale check still reached the unconditional Timer(...) at the end of
_maybeEmitStatusUpdate, overwriting _timerHandle with an untracked
timer while the fresh subscription's own timer kept running
unreferenced. Return immediately on a version mismatch so stale work
skips both emission and rescheduling.

Also bump _subscriptionVersion synchronously before the first await in
_handleStatusChangeCancel, so a resubscribe that races the in-flight
trigger-subscription cancellation can't observe the stale version.
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.

3 participants