Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

_DefaultTabControllerState should dispose all created TabContoller instances. #136608

Merged
merged 4 commits into from Oct 19, 2023

Conversation

ksokolovskyi
Copy link
Contributor

Description

Tests

  • Updates material/tabs_test.dart to use testWidgetsWithLeakTracking.

Pre-launch Checklist

  • I read the [Contributor Guide] and followed the process outlined there for submitting PRs.
  • I read the [Tree Hygiene] wiki page, which explains my responsibilities.
  • I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement].
  • I signed the [CLA].
  • I listed at least one issue that this PR fixes in the description above.
  • I updated/added relevant documentation (doc comments with ///).
  • I added new tests to check the change I am making, or this PR is [test-exempt].
  • All existing and new tests are passing.

@github-actions github-actions bot added framework flutter/packages/flutter repository. See also f: labels. f: material design flutter/packages/flutter/material repository. labels Oct 15, 2023
@ksokolovskyi
Copy link
Contributor Author

cc @polina-c

///
/// This instance of [TabController] must not be used anymore and has to be
/// disposed since [AnimationController] was injected into the new
/// [TabController].
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this true? May be it is up to invoker if the instance can be used or not?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

To be honest, usage of the TabController after the removal of AnimationController from it (after the call of _copyWith) is impossible because we have several “bang operators” which will throw:

_animationController!.value = index.toDouble();


_animationController!.value = _index.toDouble();

double get offset => _animationController!.value - _index.toDouble();

_animationController!.value = value + _index.toDouble();

Copy link
Contributor

Choose a reason for hiding this comment

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

This method does not do anything with the original instance, that makes it unusable, so it should not have opinion about what to do with it.

I suggest:

  1. Move _animationController = null; out of this method to the invokers, because the methods name does not assume any updates like this.
  2. Revert update do the comment.

How does it sound?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@polina-c, thanks for the suggestions. According to what you suggested, here is the new code:

@override
void didUpdateWidget(DefaultTabController oldWidget) {
  super.didUpdateWidget(oldWidget);
  if (oldWidget.length != widget.length) {
    ...
    final TabController newController = _controller._copyWith...
    _controller._animationController = null;
    _controller.dispose();
    _controller = newController;
  }

  if (oldWidget.animationDuration != widget.animationDuration) {
    final TabController newController = _controller._copyWith...
    _controller._animationController = null;
    _controller.dispose();
    _controller = newController;
  }
}

What do you think?
Does explicit nulling of the private member look good to you?

@polina-c polina-c added the a: leak tracking Issues and PRs related to memory leaks detected by leak_tracker label Oct 15, 2023
length: widget.length,
animationDuration: widget.animationDuration,
index: newIndex,
previousIndex: previousIndex,
);
_controller.dispose();
_controller = newController;
}

Copy link
Contributor

Choose a reason for hiding this comment

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

How about making it more readable:

TabController newController = _controller;

if (oldWidget.length != widget.length) {
  newController = newController._copyWith...
}

if (oldWidget.animationDuration != widget.animationDuration) {
  newController = newController._copyWith...
}

if (newController != _controller) {
  _controller.dispose();
  _controller = newController;
  _animationController = null;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@polina-c, thanks a lot for your suggestion, it looks much cleaner. But if we do it in such a way, then we will lose the reference to the first new TabController if the length and animationDuration are changed at the same time.

Copy link
Contributor

Choose a reason for hiding this comment

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

Really? Both _copyWith are invoked for newController, not for _controller. so, nothing is lost.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Maybe I am missing something, but here is how I see the flow in case both length and animationDuration was updated:

// newController = TabController0
TabController newController = _controller;

if (oldWidget.length != widget.length) {
  // newController = TabController1
  newController = newController._copyWith...
}

if (oldWidget.animationDuration != widget.animationDuration) {
  // newController = TabController2
  newController = newController._copyWith...
}

if (newController != _controller) {
  _controller._animationController = null;
  // TabController0.dispose()
  _controller.dispose();
  // _controller = TabController2
  _controller = newController;

 // TabController1 is lost
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, I see now.
You are right.
Thanks for explanation.

Then the only question is about _animationController = null;:

  1. Why we need it? Old controller is not referenced and is disposed, so what's the point to null _animationController?
  2. If it is needed, (1) it does not seem _copyWith is right method to do it and (2) there should be comment that explains why it is needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the review.

Under the hood, _copyWith transfers AnimationController from one TabController to another.

  /// Creates a new [TabController] with `index`, `previousIndex`, `length`, and
  /// `animationDuration` if they are non-null.
  ///
  /// This method is used by [DefaultTabController].
  ///
  /// When [DefaultTabController.length] is updated, this method is called to
  /// create a new [TabController] without creating a new [AnimationController].
  TabController _copyWith({
  ...
  }) {
   ...
    return TabController._(
      ...
      animationController: _animationController,
      ....
    );
  }

In dispose method of the TabController we have disposal of the AnimationController.

  @override
  void dispose() {
    _animationController?.dispose();
    _animationController = null;
    super.dispose();
  }

We can't hold the same AnimationController in both TabControllers because it will cause double disposal in the future. So, to not dispose recently injected AnimationController in the old TabController, we need to null it. It will be disposed in the new TabController in the future.

This explains why I added nulling of the _animationController in the _copyWith method and added this comment:

  /// This instance of [TabController] must not be used anymore and has to be
  /// disposed since [AnimationController] was injected into the new
  /// [TabController].

What can you suggest to improve or change?

Copy link
Contributor

Choose a reason for hiding this comment

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

Got it. Thanks.

Then _copyWith is now doing way more than just copying.
How about renaming _copyWith to _copyWithAndDispose, and making it fully disposing the original instance?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Great suggestion, thanks!
Pushed the new version.

@ksokolovskyi
Copy link
Contributor Author

@polina-c, thanks for help and review!

@polina-c polina-c merged commit 9fa9fd3 into flutter:master Oct 19, 2023
62 checks passed
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Oct 20, 2023
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Oct 20, 2023
auto-submit bot pushed a commit to flutter/packages that referenced this pull request Oct 20, 2023
flutter/flutter@c2bd2c1...0883cb2

2023-10-20 engine-flutter-autoroll@skia.org Roll Packages from 09c6b11 to be915be (6 revisions) (flutter/flutter#136964)
2023-10-20 engine-flutter-autoroll@skia.org Roll Flutter Engine from de780872533c to 9b88ff83fd82 (4 revisions) (flutter/flutter#136959)
2023-10-20 engine-flutter-autoroll@skia.org Roll Flutter Engine from 1e107c21328a to de780872533c (1 revision) (flutter/flutter#136952)
2023-10-20 engine-flutter-autoroll@skia.org Roll Flutter Engine from fd33e7e75eac to 1e107c21328a (1 revision) (flutter/flutter#136949)
2023-10-20 engine-flutter-autoroll@skia.org Roll Flutter Engine from 9dd59f7fcff9 to fd33e7e75eac (1 revision) (flutter/flutter#136942)
2023-10-20 engine-flutter-autoroll@skia.org Roll Flutter Engine from cc3356c0e68f to 9dd59f7fcff9 (1 revision) (flutter/flutter#136935)
2023-10-20 engine-flutter-autoroll@skia.org Roll Flutter Engine from 504a99d93f42 to cc3356c0e68f (2 revisions) (flutter/flutter#136934)
2023-10-19 engine-flutter-autoroll@skia.org Roll Flutter Engine from 503b84295462 to 504a99d93f42 (3 revisions) (flutter/flutter#136930)
2023-10-19 engine-flutter-autoroll@skia.org Roll Flutter Engine from 3b0469b0c718 to 503b84295462 (1 revision) (flutter/flutter#136928)
2023-10-19 katelovett@google.com Revert "[Velocity Tracker] Fix: Issue 97761: Flutter Scrolling does not match iOS; inadvertent scrolling when user lifts up finger" (flutter/flutter#136905)
2023-10-19 christopherfujino@gmail.com [flutter_tools] move build_preview_test from commands/permeable to integration shard (flutter/flutter#136912)
2023-10-19 engine-flutter-autoroll@skia.org Roll Flutter Engine from b40042ebb95b to 3b0469b0c718 (2 revisions) (flutter/flutter#136922)
2023-10-19 andrewrkolos@gmail.com do not include entries from `--dart-define-from-file` files in the gradle config or environment during build (flutter/flutter#136865)
2023-10-19 engine-flutter-autoroll@skia.org Roll Flutter Engine from bfd2ffb9a8bc to b40042ebb95b (2 revisions) (flutter/flutter#136915)
2023-10-19 engine-flutter-autoroll@skia.org Roll Flutter Engine from 9d49175618f5 to bfd2ffb9a8bc (2 revisions) (flutter/flutter#136910)
2023-10-19 36861262+QuncCccccc@users.noreply.github.com Allow users to customize search algorithm in `DropdownMenu` (flutter/flutter#136848)
2023-10-19 21270878+elliette@users.noreply.github.com Upgrade Flutter deps to pull in latest vm_service and dwds (flutter/flutter#136734)
2023-10-19 15619084+vashworth@users.noreply.github.com [Reland] Skip injecting Bonjour settings when port publication is disabled (flutter/flutter#136842)
2023-10-19 sokolovskyi.konstantin@gmail.com _DefaultTabControllerState should dispose all created TabContoller instances. (flutter/flutter#136608)
2023-10-19 gspencergoog@users.noreply.github.com Reland: "Add code for updating `focusedChild` when removing grandchildren from scope" (flutter/flutter#136899)
2023-10-19 engine-flutter-autoroll@skia.org Roll Flutter Engine from 418dce4feaf7 to 9d49175618f5 (1 revision) (flutter/flutter#136901)
2023-10-19 34871572+gmackall@users.noreply.github.com Unmark linux_android platform_channels_benchmarks as flaky (flutter/flutter#136838)
2023-10-19 engine-flutter-autoroll@skia.org Roll Packages from 14aa69e to 09c6b11 (4 revisions) (flutter/flutter#136896)
2023-10-19 engine-flutter-autoroll@skia.org Roll Flutter Engine from 28cb2508b8e0 to 418dce4feaf7 (2 revisions) (flutter/flutter#136895)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-packages
Please CC rmistry@google.com,stuartmorgan@google.com,tarrinneal@google.com on the revert to ensure that a human
is aware of the problem.

To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
HugoOlthof pushed a commit to moneybird/packages that referenced this pull request Dec 13, 2023
…r#5192)

flutter/flutter@c2bd2c1...0883cb2

2023-10-20 engine-flutter-autoroll@skia.org Roll Packages from 09c6b11 to be915be (6 revisions) (flutter/flutter#136964)
2023-10-20 engine-flutter-autoroll@skia.org Roll Flutter Engine from de780872533c to 9b88ff83fd82 (4 revisions) (flutter/flutter#136959)
2023-10-20 engine-flutter-autoroll@skia.org Roll Flutter Engine from 1e107c21328a to de780872533c (1 revision) (flutter/flutter#136952)
2023-10-20 engine-flutter-autoroll@skia.org Roll Flutter Engine from fd33e7e75eac to 1e107c21328a (1 revision) (flutter/flutter#136949)
2023-10-20 engine-flutter-autoroll@skia.org Roll Flutter Engine from 9dd59f7fcff9 to fd33e7e75eac (1 revision) (flutter/flutter#136942)
2023-10-20 engine-flutter-autoroll@skia.org Roll Flutter Engine from cc3356c0e68f to 9dd59f7fcff9 (1 revision) (flutter/flutter#136935)
2023-10-20 engine-flutter-autoroll@skia.org Roll Flutter Engine from 504a99d93f42 to cc3356c0e68f (2 revisions) (flutter/flutter#136934)
2023-10-19 engine-flutter-autoroll@skia.org Roll Flutter Engine from 503b84295462 to 504a99d93f42 (3 revisions) (flutter/flutter#136930)
2023-10-19 engine-flutter-autoroll@skia.org Roll Flutter Engine from 3b0469b0c718 to 503b84295462 (1 revision) (flutter/flutter#136928)
2023-10-19 katelovett@google.com Revert "[Velocity Tracker] Fix: Issue 97761: Flutter Scrolling does not match iOS; inadvertent scrolling when user lifts up finger" (flutter/flutter#136905)
2023-10-19 christopherfujino@gmail.com [flutter_tools] move build_preview_test from commands/permeable to integration shard (flutter/flutter#136912)
2023-10-19 engine-flutter-autoroll@skia.org Roll Flutter Engine from b40042ebb95b to 3b0469b0c718 (2 revisions) (flutter/flutter#136922)
2023-10-19 andrewrkolos@gmail.com do not include entries from `--dart-define-from-file` files in the gradle config or environment during build (flutter/flutter#136865)
2023-10-19 engine-flutter-autoroll@skia.org Roll Flutter Engine from bfd2ffb9a8bc to b40042ebb95b (2 revisions) (flutter/flutter#136915)
2023-10-19 engine-flutter-autoroll@skia.org Roll Flutter Engine from 9d49175618f5 to bfd2ffb9a8bc (2 revisions) (flutter/flutter#136910)
2023-10-19 36861262+QuncCccccc@users.noreply.github.com Allow users to customize search algorithm in `DropdownMenu` (flutter/flutter#136848)
2023-10-19 21270878+elliette@users.noreply.github.com Upgrade Flutter deps to pull in latest vm_service and dwds (flutter/flutter#136734)
2023-10-19 15619084+vashworth@users.noreply.github.com [Reland] Skip injecting Bonjour settings when port publication is disabled (flutter/flutter#136842)
2023-10-19 sokolovskyi.konstantin@gmail.com _DefaultTabControllerState should dispose all created TabContoller instances. (flutter/flutter#136608)
2023-10-19 gspencergoog@users.noreply.github.com Reland: "Add code for updating `focusedChild` when removing grandchildren from scope" (flutter/flutter#136899)
2023-10-19 engine-flutter-autoroll@skia.org Roll Flutter Engine from 418dce4feaf7 to 9d49175618f5 (1 revision) (flutter/flutter#136901)
2023-10-19 34871572+gmackall@users.noreply.github.com Unmark linux_android platform_channels_benchmarks as flaky (flutter/flutter#136838)
2023-10-19 engine-flutter-autoroll@skia.org Roll Packages from 14aa69e to 09c6b11 (4 revisions) (flutter/flutter#136896)
2023-10-19 engine-flutter-autoroll@skia.org Roll Flutter Engine from 28cb2508b8e0 to 418dce4feaf7 (2 revisions) (flutter/flutter#136895)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-packages
Please CC rmistry@google.com,stuartmorgan@google.com,tarrinneal@google.com on the revert to ensure that a human
is aware of the problem.

To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
goderbauer added a commit to goderbauer/flutter that referenced this pull request Mar 5, 2024
goderbauer added a commit to goderbauer/flutter that referenced this pull request Mar 5, 2024
auto-submit bot pushed a commit that referenced this pull request Mar 5, 2024
…oller instances. (#136608)" (#144579)

This reverts commit 9fa9fd3.

Fixes #144087.
Fixes #138588.

This crash has been reported previously from a customer in google3 in #138588, but we weren't able to track it down. Thankfully, a repro was now provided in #144087 (comment) which traced the crash down to a change made in #136608. This PR reverts that change to fix that crash for now. I will post a new PR shortly that will add a test to cover the use case that caused the crash with #136608 to make sure we don't re-introduce the crash in the future.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
a: leak tracking Issues and PRs related to memory leaks detected by leak_tracker f: material design flutter/packages/flutter/material repository. framework flutter/packages/flutter repository. See also f: labels.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Memory Leak: _DefaultTabControllerState doesn't dispose created TabController.
2 participants