8000 fix(app): release the live session on dispose and stop background polling by shroominic · Pull Request #177 · shroominic/OpenGlucose · GitHub
[go: up one dir, main page]

Skip to content

fix(app): release the live session on dispose and stop background polling - #177

Merged
shroominic merged 2 commits into
feature/cbio-gs1-app-livefrom
fix/session-teardown-lifecycle
Sep 18, 2026
Merged

shroominic merged 2 commits into
feature/cbio-gs1-app-livefrom
fix/session-teardown-lifecycle

Conversation

@shroominic
Copy link
Copy Markdown
Owner

Closes #176.

The defect

Lifetime Who ends it today Who should
controller disposed nobody dispose()
app stops being presented nobody lifecycle handler

CgmAppController.dispose() cancelled the scan iterator, reconnect timer,
history-persist timer and both subscriptions, then left _session alive.
_CgmHomePageState.didChangeAppLifecycleState returned early for every state
except resumed, so the 45 s _foregroundFreshnessInterval timer kept calling
ensureFreshData() - which connects when no session is held.

RED

cd openhealth && flutter test test/app_controller_persistence_test.dart --plain-name 'disconnects its live session'
exit 1
Expected: <1>
  Actual: <0>
  a disposed controller must not leave a GATT client open
test/app_controller_persistence_test.dart 2570:5
cd openhealth && flutter test test/app_controller_persistence_test.dart --plain-name 'backgrounded app stops foreground polling'
exit 1
Expected: <1>
  Actual: <3>
  a backgrounded app must not keep driving its session
test/app_controller_persistence_test.dart 2623:5

Both runs are against 17db5b4, the base this branch forks.

GREEN

Same two commands on this branch: exit 0, +1: All tests passed! each.

The fix

  • dispose() takes _session, clears it, and hands it to
    session.disconnect() with the same fail-soft shape the subscription cancels
    already use: unawaited, errors swallowed. Nothing else claimed ownership, so a
    throw here has no reporter.
  • main.dart replaces the resumed-only early return with a full switch:
    • resumed - restart the foreground poll, then ensureFreshData(force: true)
      as before.
    • inactive - no work. This is a permission sheet, the app switcher or a
      call; the user has not left the app.
    • hidden / paused / detached - stop the foreground poll.
  • _startForegroundFreshness / _stopForegroundFreshness are the only places
    that touch _freshnessTimer, so dispose() and the lifecycle handler cannot
    drift apart.

What this deliberately does not do

It does not disconnect the session on background. Three pieces of the
existing design say not to:

  • openhealth/ios/Runner/Info.plist declares UIBackgroundModes: bluetooth-central, so a held link is a supported background state, not a leak.
  • The lock-screen surface is fed from that same session: disconnect() pushes
    _pushLiveActivity(), and a disconnected snapshot is not publishable, so
    tearing down on background would end() the Live Activity every time the user
    leaves the app.
  • AidexBackgroundMonitor scans natively only while !isAppActive, which is a
    hand-off model: it is only coherent if the Dart session is allowed to persist.

The leak in the background path is the poll, not the link, so the poll is what
was removed.

Verification

  • flutter test test/app_controller_persistence_test.dart - exit 0, +51
  • flutter test - exit 0, +482: All tests passed!
  • flutter analyze - No issues found!
  • dart format --output=none --set-exit-if-changed lib/src/app_controller.dart test/app_controller_persistence_test.dart - clean

lib/main.dart still carries one pre-existing, unrelated formatting drift
(lines 1131-1133, the 8000 "Latest reading at ..." expression) that a whole-file
dart format rewrites. It is on another lane and is left exactly as found; this
change is confined to the lifecycle region.

@shroominic
Copy link
Copy Markdown
Owner Author

Rebase reminder (comment only, no edits to this branch).

The lane head is now 05785b0 (#192 merged). This PR is still based on 17db5b4; the merge-base is fine, but expect a conflict in
packages/cgm_cbio/lib/src/cbio_glucose_session.dart around credential resolution. Resolve it by keeping the injected path:

  • the session resolves CbioCredentials once per session into _resolved, before the transport is touched, and every mask/unmask/builder call receives _streamKey/the material explicitly;
  • CbioCompiledAuthMaterialProvider and the compiled constants are gone; a build without material fails closed with cbio.auth.material;
  • the lane also reads _clockReferenceEpochSeconds (set in _writeClock) when it derives the record-index anchor, so keep that field and the _historyReadingsFor(anchor) / metadata spread when you resolve.

Rebase onto feature/cbio-gs1-app-live before review so CI runs against the lane head.

@shroominic
Copy link
Copy Markdown
Owner Author

Rebased onto the lane tip 9de5544 (05785b0 + #193 + #197) and force-pushed. No conflicts: this PR's main.dart hunk is in the home-page lifecycle state, while the lane's new hunk is in the hero card.

Verification on the rebased head: openhealth flutter test 490 passed, flutter analyze clean, dart format clean. One full-suite run reported a single failure before the fix was touched again; three further runs (including the whole suite twice and app_controller_persistence_test.dart three times) were green, so it looks like a load-sensitive flake rather than a regression - flagged here rather than hidden.

@shroominic
shroominic force-pushed the fix/session-teardown-lifecycle branch from 013a36c to 674470d Compare September 18, 2026 09:31
@shroominic
shroominic force-pushed the fix/session-teardown-lifecycle branch from 674470d to 577fd3d Compare September 18, 2026 09:41
@shroominic
shroominic merged commit 48e4d81 into feature/cbio-gs1-app-live Sep 18, 2026
5 of 10 checks passed
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.

1 participant

0