You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix(polling): drop stale in-flight check results on cancel/resubscribe - #148
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.
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.
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.
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.
The `option` local was referenced only once, in `customCheckOptions`.
The `option:` argument inside `customConnectivityCheck` comes from the
callback's own `opt` parameter, not this variable. Inline it to match
how every other test in the file builds its check options.
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
The cancellation test passes even with the pre-fix implementation because it checks connectivity calls rather than timer creation, leaving the unintended polling-timer restart undetected.
No open issues 🎉
1 previously missed issue
In code that hasn't changed since last review
Test does not detect polling timer restart after cancellation
test/internet_connection_test.dart:357
This assertion also passes with the pre-fix implementation: the cancelled check schedules a timer, but its callback returns at if (!_statusController.hasListener) return; before invoking another connectivity check, so checkCount remains 1. The test therefore does not detect the timer restart it is intended to prevent. Track timer creation (for example, with ZoneSpecification.createTimer) and assert that completing the cancelled check schedules no polling timer.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Split out from #139 per review feedback, with
_cancelGenerationrenamed 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:
This adds a
_subscriptionVersioncounter, bumped only when a listener cancels._maybeEmitStatusUpdatesnapshots 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 issuesdart format --set-exit-if-changed— cleandart test— all tests pass, including two new regression tests: