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

fix(Win32): PopupImpl memory leak #16890

Merged

Conversation

workgroupengineering
Copy link
Contributor

@workgroupengineering workgroupengineering commented Sep 2, 2024

What does the pull request do?

Fix memory leak when OverlayPopups is false and popup opens, PopupImpl is not released when popup closes.

What is the current behavior?

PopupImpl is not released when popup closes.

What is the updated/expected behavior with this PR?

How was the solution implemented (if it's not obvious)?

  • Removed closures from TopLevel, WindowBase and Window that retention PopupImpl
  • Avoid PopupImpl retention derived from cross-reference from Avalonia.Win32/WindowImpl and WindowsInputPane

Checklist

Breaking changes

Obsoletions / Deprecations

Fixed issues

@workgroupengineering workgroupengineering marked this pull request as ready for review September 2, 2024 17:00
@avaloniaui-bot
Copy link

You can test this PR using the following package version. 11.2.999-cibuild0051610-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

{
onValue(GetValue(property));
Copy link
Member

@kekekeks kekekeks Sep 3, 2024

Choose a reason for hiding this comment

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

I don't quite see the point of altering the callback signature. It's only possible to call CreatePlatformImplBinding from the same object instance, and any callbacks would be referencing this rather than the PlatformImpl value captured during callback creation.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The closure creates a cross reference that does not allow the PopupImpl to be collected

To test

  1. Apply Sandbox Patch SandBox-Test-Patch.patch
  2. Build Sandbox in Release mode
  3. Run dotMemoery
  4. Make memory Snapshot (Snapshot 1)
  5. Click on Button e
  6. Dismiss popup with click on window
  7. Repeat steps 5, 6 ten times.
  8. Make memory Snapshot (Snapshot 2)
  9. Force GC Collect
  10. Make memory Snapshot (Snapshot 3)
  11. Kill Sandbox
  12. Compare Snapshot 2 and 3
  13. Filter for PopupImp
  14. You can see there is only ManagedPopupPositionerPopupImplHelper+<>c
  15. Revert some change with:
      git checkout 7041139ee0556c474dad7dc599c0b69bcdf5d1b7~ -- "src/Avalonia.Controls/WindowBase.cs"
      git checkout 7041139ee0556c474dad7dc599c0b69bcdf5d1b7~ -- "src/Avalonia.Controls/Window.cs"
      git checkout 7041139ee0556c474dad7dc599c0b69bcdf5d1b7~ -- "src/Avalonia.Controls/TopLevel.cs"
  16. Repeat steps from 2 to 13
  17. In in the list besides ManagedPopupPositionerPopupImplHelper there is a PopupImpl instance
  18. If you step into you can see that retained from Avalonia.Controls.TopLevel+<>c__DisplayClass33_0.impl
    immagine

Copy link
Member

Choose a reason for hiding this comment

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

It's the closure that's passed to CompositionTarget, not the one related to CreatePlatformImplBinding. The closures passed to CreatePlatformImplBinding are consuming PlatformImpl property and are capturing this.

Copy link
Member

Choose a reason for hiding this comment

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

BTW, the leak itself is about keeping a reference to a disposed PopupRoot, rather than about TopLevel keeping references to its internals after being disposed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So I revert TopLevel except for line 222?

Copy link
Member

Choose a reason for hiding this comment

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

You can replace line 222 with Renderer = new CompositingRenderer(this, impl.Compositor, () => PlatformImpl.Surfaces ?? []);, it will capture this and use the property. The rest of the TopLevel changes seems to be not needed.

@avaloniaui-bot
Copy link

You can test this PR using the following package version. 11.2.999-cibuild0051621-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@workgroupengineering
Copy link
Contributor Author

Do I need to make any other changes?

Comment on lines +613 to +614
_inputPane?.Dispose();
_inputPane = null;
Copy link
Member

Choose a reason for hiding this comment

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

We already dispose pane on WM_DESTROY. And it should be guaranteed to be called unless process was terminated (in which case we don't care about disposing).

Copy link
Member

Choose a reason for hiding this comment

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

Other than that lgtm

@avaloniaui-bot
Copy link

You can test this PR using the following package version. 11.2.999-cibuild0052254-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@avaloniaui-bot
Copy link

You can test this PR using the following package version. 11.2.999-cibuild0052256-alpha. (feed url: https://nuget-feed-all.avaloniaui.net/v3/index.json) [PRBUILDID]

@maxkatz6 maxkatz6 added this pull request to the merge queue Sep 30, 2024
Merged via the queue into AvaloniaUI:master with commit 182e3f9 Sep 30, 2024
10 checks passed
@workgroupengineering workgroupengineering deleted the fix/Platform/Windows/PopupImpl branch September 30, 2024 15:17
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.

4 participants