[camera] Fix CameraController may update after dispose and crash with "used after being disposed. (fixes #184959)#11498
[camera] Fix CameraController may update after dispose and crash with "used after being disposed. (fixes #184959)#11498Istiak-Ahmed78 wants to merge 3 commits intoflutter:mainfrom
Conversation
| .onDeviceOrientationChanged() | ||
| .listen((DeviceOrientationChangedEvent event) { | ||
| value = value.copyWith(deviceOrientation: event.orientation); | ||
| if (!_isDisposed) { |
There was a problem hiding this comment.
Could these use early returns? That would reduce the indent changes in code.
Just a personal preference though.
There was a problem hiding this comment.
Thanks for the suggestion! Early returns would definitely improve the code.
However, this PR is still in draft. @mbcorona from the Flutter team recently
discovered a deeper issue: ImageReader fails to drain pending frames on dispose,
flooding the queue with 290+ orphaned callbacks.
flutter/flutter#184959 (comment)
My current fix addresses the symptom (guarding state updates). but the root cause is the improper ImageReader cleanup at the native
level. So I'm currently reinvestigating the issue
Once I resolve the frame leakage issue, I'll:
- Apply your early return suggestion
- Update the fix comprehensively
- Mark it ready for review
Thanks again for your suggestion!
There was a problem hiding this comment.
Before marking it ready for review, please update the PR to use and follow our actual checklist, rather than an AI-hallucinated checklist.
Fixes flutter/flutter#184959
Description
Fixed a critical race condition bug in the camera package where async callbacks in
CameraController._initializeWithDescription()could execute afterdispose()is called, causing a crash:"A CameraController was used after being disposed. Once you have called dispose() on a CameraController, it can no longer be used."Root Cause
In
_initializeWithDescription(), three async fire-and-forget callbacks had no dispose guards:The Issue: When
dispose()is called while these callbacks are still pending:dispose()sets_isDisposed = truevalue = ...which callsnotifyListeners()notifyListeners()throws because the controller is disposedSolution
Added
if (!_isDisposed)guards before everyvalue = ...assignment in async contexts within_initializeWithDescription().Pre-launch Checklist