Skip to content

[leak-fix] Fix Path.Data/RenderTransform memory leak (Fixes #35860)#36273

Open
kubaflo wants to merge 4 commits into
dotnet:mainfrom
kubaflo:promote/path-data-geometry-leak
Open

[leak-fix] Fix Path.Data/RenderTransform memory leak (Fixes #35860)#36273
kubaflo wants to merge 4 commits into
dotnet:mainfrom
kubaflo:promote/path-data-geometry-leak

Conversation

@kubaflo

@kubaflo kubaflo commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Note

Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!

Note

🤖 AI-generated PR. This fix and its regression tests were produced by an AI-driven memory-leak workflow and verified locally. Please review carefully before merging.

Fixes #35860

The leak

Path.Data and Path.RenderTransform subscribed the Path to change notifications on the assigned geometry or transform using strong handlers. A shared or long-lived value could therefore retain every Path using it and, through the Path, its visual tree or page.

The fix

Subscribe through WeakGeometryChangedProxy and WeakNotifyPropertyChangedProxy, owned by a private, lazily-created PathChangeSubscriptions helper. The helper finalizer tears down the proxies when the owner is collected, preventing shared geometry and transform instances from rooting the Path without adding a public finalizer or changing the public API surface.

Regression tests

PathMemoryTests in src/Controls/tests/Core.UnitTests/PathMemoryTests.cs covers:

  • PathDataDoesNotLeakWhenGeometryOutlivesIt
  • PathRenderTransformDoesNotLeakWhenTransformOutlivesIt
  • PathDataChangesStillNotifyAfterGc
  • PathRenderTransformChangesStillNotifyAfterGc
  • PathDataReassignmentMovesChangeSubscription
  • PathRenderTransformReassignmentMovesChangeSubscription

Verified locally against this PR base (main), Controls.Core.UnitTests:

State Result
Without fix ❌ leak regressions fail
With fix ✅ targeted PathMemoryTests pass

Scope

Managed cross-platform change for all platforms. No public API additions.

)

Subscribe to a shared Geometry (Path.Data) and Transform (Path.RenderTransform) through weak proxies, torn down via a finalizer, so a shared/long-lived app-level PathGeometry or ScaleTransform no longer roots every Path. Adds Controls.Core.UnitTests regression tests that fail without this fix.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 36273

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 36273"

@kubaflo

This comment has been minimized.

@github-actions github-actions Bot added the area-drawing Shapes, Borders, Shadows, Graphics, BoxView, custom drawing label Jul 1, 2026
@github-actions github-actions Bot added the s/agent-review-in-progress AI review is currently running for this PR label Jul 1, 2026
@MauiBot MauiBot removed the s/agent-review-in-progress AI review is currently running for this PR label Jul 1, 2026
@kubaflo

This comment has been minimized.

@github-actions github-actions Bot added the s/agent-review-in-progress AI review is currently running for this PR label Jul 2, 2026
@MauiBot MauiBot added s/agent-gate-passed AI verified tests catch the bug (fail without fix, pass with fix) s/agent-fix-pr-picked AI could not beat the PR fix - PR is the best among all candidates s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review) labels Jul 2, 2026
MauiBot

This comment was marked as outdated.

@MauiBot MauiBot removed the s/agent-review-in-progress AI review is currently running for this PR label Jul 2, 2026
@kubaflo kubaflo marked this pull request as ready for review July 2, 2026 21:59
Copilot AI review requested due to automatic review settings July 2, 2026 21:59
@kubaflo

kubaflo commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 3 pipeline(s).

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR addresses a managed memory leak in Microsoft.Maui.Controls.Shapes.Path where assigning long-lived/shared Geometry (Path.Data) or Transform (Path.RenderTransform) could keep Path instances (and therefore their visual trees/pages) alive via strong event subscriptions. The fix aligns Path with existing weak-event proxy patterns already used in Controls.

Changes:

  • Replaced strong event subscriptions for Path.Data and Path.RenderTransform with weak-event proxies, and ensured proxies are unsubscribed during finalization.
  • Added new Controls.Core.UnitTests regression tests to ensure Path can be collected even when the assigned geometry/transform outlives it.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
src/Controls/src/Core/Shapes/Path.cs Switches Data/RenderTransform change notification wiring to weak proxies and unsubscribes proxies in ~Path() to avoid rooting Path via shared resources.
src/Controls/tests/Core.UnitTests/PathMemoryTests.cs Adds GC-based regression tests validating Path does not leak when using long-lived shared Geometry/Transform.

Comment thread src/Controls/tests/Core.UnitTests/PathMemoryTests.cs
Comment thread src/Controls/tests/Core.UnitTests/PathMemoryTests.cs
@kubaflo

This comment has been minimized.

@github-actions github-actions Bot added the s/agent-review-in-progress AI review is currently running for this PR label Jul 3, 2026
@kubaflo

This comment has been minimized.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: bbcdaa9d-d75a-4e3b-b375-cf827a689644
Copilot AI review requested due to automatic review settings July 11, 2026 21:15
@kubaflo

kubaflo commented Jul 11, 2026

Copy link
Copy Markdown
Contributor Author

🔍 AI-generated update (GitHub Copilot CLI, on behalf of @kubaflo)

@copilot-pull-request-reviewer Both new findings are addressed: the explicit geometry event overload is pushed in eef2147a0b, and the PR description now matches the private-helper implementation. Ready for re-review.

@kubaflo

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

@MauiBot MauiBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Expert Review — 1 findings

See inline comments for details.

Comment thread src/Controls/tests/Core.UnitTests/PathMemoryTests.cs
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: bbcdaa9d-d75a-4e3b-b375-cf827a689644
Copilot AI review requested due to automatic review settings July 11, 2026 21:45
@kubaflo

kubaflo commented Jul 11, 2026

Copy link
Copy Markdown
Contributor Author

🔍 AI-generated update (GitHub Copilot CLI, on behalf of @kubaflo)

@MauiBot The reassignment coverage is added in 914e625200, the thread is resolved, and the PR description is current. Ready for re-review.

@kubaflo

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.

MauiBot

This comment was marked as outdated.

@MauiBot MauiBot removed the s/agent-review-in-progress AI review is currently running for this PR label Jul 11, 2026
@kubaflo

kubaflo commented Jul 11, 2026

Copy link
Copy Markdown
Contributor Author

🔍 AI-generated response (GitHub Copilot CLI, on behalf of @kubaflo)

@MauiBot The reassignment suggestion and metadata note are already addressed at current head 914e625200: PathDataReassignmentMovesChangeSubscription and PathRenderTransformReassignmentMovesChangeSubscription cover old-source detachment, and the current description documents PathChangeSubscriptions, both tests, and no public API change. No no-op commit or duplicate review trigger was posted.

@kubaflo

This comment has been minimized.

@github-actions github-actions Bot added the s/agent-review-in-progress AI review is currently running for this PR label Jul 12, 2026
MauiBot

This comment was marked as outdated.

@MauiBot MauiBot removed the s/agent-review-in-progress AI review is currently running for this PR label Jul 12, 2026
@kubaflo

kubaflo commented Jul 12, 2026

Copy link
Copy Markdown
Contributor Author

🔍 AI-generated review follow-up (GitHub Copilot CLI, on behalf of @kubaflo)

@MauiBot The current-head review reports no errors or actionable findings. Its finalizer warning explicitly accepts the private lifecycle pattern and confirms the leak, reassignment, post-GC notification, and null-assignment paths are covered, so no code change is warranted. Ready for another review.

@kubaflo

This comment has been minimized.

@github-actions github-actions Bot added the s/agent-review-in-progress AI review is currently running for this PR label Jul 12, 2026

@MauiBot MauiBot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

AI Review Summary

@kubaflo — new AI review results are available based on this last commit: 914e625. To request a fresh review after new comments or commits, comment /review rerun.

Gate Passed Confidence Low Platform Android


🗂️ Review Sessions — click to expand
🚦 Gate — Test Before & After Fix

Gate Result: ✅ PASSED

Platform: ANDROID · Base: main · Merge base: 0395a53b

Test Without Fix (expect FAIL) With Fix (expect PASS)
🧪 PathMemoryTests PathMemoryTests ✅ FAIL — 114s ✅ PASS — 82s
🔴 Without fix — 🧪 PathMemoryTests: FAIL ✅ · 114s
  Determining projects to restore...
  Restored /home/vsts/work/1/s/src/Controls/tests/Core.UnitTests/Controls.Core.UnitTests.csproj (in 5.78 sec).
  Restored /home/vsts/work/1/s/src/Controls/Maps/src/Controls.Maps.csproj (in 9.28 sec).
  Restored /home/vsts/work/1/s/src/Controls/src/Xaml/Controls.Xaml.csproj (in 1.43 sec).
  Restored /home/vsts/work/1/s/src/TestUtils/src/TestUtils/TestUtils.csproj (in 6 ms).
  Restored /home/vsts/work/1/s/src/Graphics/src/Graphics/Graphics.csproj (in 18 ms).
  Restored /home/vsts/work/1/s/src/Controls/src/Core/Controls.Core.csproj (in 60 ms).
  Restored /home/vsts/work/1/s/src/Essentials/src/Essentials.csproj (in 29 ms).
  Restored /home/vsts/work/1/s/src/Core/maps/src/Maps.csproj (in 37 ms).
  Restored /home/vsts/work/1/s/src/Core/src/Core.csproj (in 71 ms).
  1 of 10 projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.100-ci+azdo.14634860
  Graphics -> /home/vsts/work/1/s/artifacts/bin/Graphics/Debug/net10.0/Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.100-ci+azdo.14634860
  Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.100-ci+azdo.14634860
  Core -> /home/vsts/work/1/s/artifacts/bin/Core/Debug/net10.0/Microsoft.Maui.dll
  Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.100-ci+azdo.14634860
  Maps -> /home/vsts/work/1/s/artifacts/bin/Maps/Debug/net10.0/Microsoft.Maui.Maps.dll
  ##vso[build.updatebuildnumber]10.0.100-ci+azdo.14634860
  Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.Core/Debug/net10.0/Microsoft.Maui.Controls.dll
  ##vso[build.updatebuildnumber]10.0.100-ci+azdo.14634860
  ##vso[build.updatebuildnumber]10.0.100-ci+azdo.14634860
  Controls.Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.Maps/Debug/net10.0/Microsoft.Maui.Controls.Maps.dll
  Controls.Xaml -> /home/vsts/work/1/s/artifacts/bin/Controls.Xaml/Debug/net10.0/Microsoft.Maui.Controls.Xaml.dll
  TestUtils -> /home/vsts/work/1/s/artifacts/bin/TestUtils/Debug/netstandard2.0/Microsoft.Maui.TestUtils.dll
  Controls.Core.UnitTests -> /home/vsts/work/1/s/artifacts/bin/Controls.Core.UnitTests/Debug/net10.0/Microsoft.Maui.Controls.Core.UnitTests.dll
Test run for /home/vsts/work/1/s/artifacts/bin/Controls.Core.UnitTests/Debug/net10.0/Microsoft.Maui.Controls.Core.UnitTests.dll (.NETCoreApp,Version=v10.0)
VSTest version 18.0.1 (x64)

Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
[xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v2.8.2+699d445a1a (64-bit .NET 10.0.0)
[xUnit.net 00:00:00.17]   Discovering: Microsoft.Maui.Controls.Core.UnitTests
[xUnit.net 00:00:01.35]   Discovered:  Microsoft.Maui.Controls.Core.UnitTests
[xUnit.net 00:00:01.37]   Starting:    Microsoft.Maui.Controls.Core.UnitTests
[xUnit.net 00:00:02.34]     PathDataDoesNotLeakWhenGeometryOutlivesIt(geometryType: typeof(Microsoft.Maui.Controls.Shapes.PathGeometry)) [FAIL]
[xUnit.net 00:00:02.37]       Path should not be alive!
[xUnit.net 00:00:02.39]       Stack Trace:
[xUnit.net 00:00:02.39]         /_/src/Controls/tests/Core.UnitTests/PathMemoryTests.cs(32,0): at Microsoft.Maui.Controls.Core.UnitTests.PathMemoryTests.PathDataDoesNotLeakWhenGeometryOutlivesIt(Type geometryType)
[xUnit.net 00:00:02.39]         --- End of stack trace from previous location ---
  Passed PathRenderTransformChangesStillNotifyAfterGc [29 ms]
[xUnit.net 00:00:03.07]     PathDataDoesNotLeakWhenGeometryOutlivesIt(geometryType: typeof(Microsoft.Maui.Controls.Shapes.RectangleGeometry)) [FAIL]
  Failed PathDataDoesNotLeakWhenGeometryOutlivesIt(geometryType: typeof(Microsoft.Maui.Controls.Shapes.PathGeometry)) [863 ms]
  Error Message:
   Path should not be alive!
  Stack Trace:
     at Microsoft.Maui.Controls.Core.UnitTests.PathMemoryTests.PathDataDoesNotLeakWhenGeometryOutlivesIt(Type geometryType) in /_/src/Controls/tests/Core.UnitTests/PathMemoryTests.cs:line 32
--- End of stack trace from previous location ---
[xUnit.net 00:00:03.07]       Path should not be alive!
[xUnit.net 00:00:03.08]       Stack Trace:
[xUnit.net 00:00:03.08]         /_/src/Controls/tests/Core.UnitTests/PathMemoryTests.cs(32,0): at Microsoft.Maui.Controls.Core.UnitTests.PathMemoryTests.PathDataDoesNotLeakWhenGeometryOutlivesIt(Type geometryType)
[xUnit.net 00:00:03.08]         --- End of stack trace from previous location ---
[xUnit.net 00:00:03.92]     PathDataDoesNotLeakWhenGeometryOutlivesIt(geometryType: typeof(Microsoft.Maui.Controls.Shapes.EllipseGeometry)) [FAIL]
[xUnit.net 00:00:03.92]       Path should not be alive!
[xUnit.net 00:00:03.92]       Stack Trace:
[xUnit.net 00:00:03.92]         /_/src/Controls/tests/Core.UnitTests/PathMemoryTests.cs(32,0): at Microsoft.Maui.Controls.Core.UnitTests.PathMemoryTests.PathDataDoesNotLeakWhenGeometryOutlivesIt(Type geometryType)
[xUnit.net 00:00:03.92]         --- End of stack trace from previous location ---
  Failed PathDataDoesNotLeakWhenGeometryOutlivesIt(geometryType: typeof(Microsoft.Maui.Controls.Shapes.RectangleGeometry)) [748 ms]
  Error Message:
   Path should not be alive!
  Stack Trace:
     at Microsoft.Maui.Controls.Core.UnitTests.PathMemoryTests.PathDataDoesNotLeakWhenGeometryOutlivesIt(Type geometryType) in /_/src/Controls/tests/Core.UnitTests/PathMemoryTests.cs:line 32
--- End of stack trace from previous location ---
  Failed PathDataDoesNotLeakWhenGeometryOutlivesIt(geometryType: typeof(Microsoft.Maui.Controls.Shapes.EllipseGeometry)) [846 ms]
  Error Message:
   Path should not be alive!
  Stack Trace:
     at Microsoft.Maui.Controls.Core.UnitTests.PathMemoryTests.PathDataDoesNotLeakWhenGeometryOutlivesIt(Type geometryType) in /_/src/Controls/tests/Core.UnitTests/PathMemoryTests.cs:line 32
--- End of stack trace from previous location ---
[xUnit.net 00:00:04.66]     PathRenderTransformDoesNotLeakWhenTransformOutlivesIt [FAIL]
[xUnit.net 00:00:04.66]       Path should not be alive!
[xUnit.net 00:00:04.66]       Stack Trace:
[xUnit.net 00:00:04.66]         /_/src/Controls/tests/Core.UnitTests/PathMemoryTests.cs(54,0): at Microsoft.Maui.Controls.Core.UnitTests.PathMemoryTests.PathRenderTransformDoesNotLeakWhenTransformOutlivesIt()
[xUnit.net 00:00:04.66]         --- End of stack trace from previous location ---
[xUnit.net 00:00:04.69]   Finished:    Microsoft.Maui.Controls.Core.UnitTests
  Failed PathRenderTransformDoesNotLeakWhenTransformOutlivesIt [730 ms]
  Error Message:
   Path should not be alive!
  Stack Trace:
     at Microsoft.Maui.Controls.Core.UnitTests.PathMemoryTests.PathRenderTransformDoesNotLeakWhenTransformOutlivesIt() in /_/src/Controls/tests/Core.UnitTests/PathMemoryTests.cs:line 54
--- End of stack trace from previous location ---
  Passed PathDataReassignmentMovesChangeSubscription [3 ms]
  Passed PathRenderTransformReassignmentMovesChangeSubscription [< 1 ms]
  Passed PathDataChangesStillNotifyAfterGc [19 ms]

Test Run Failed.
Total tests: 8
     Passed: 4
     Failed: 4
 Total time: 5.1580 Seconds

🟢 With fix — 🧪 PathMemoryTests: PASS ✅ · 82s
  Determining projects to restore...
  All projects are up-to-date for restore.
  ##vso[build.updatebuildnumber]10.0.100-ci+azdo.14634860
  Graphics -> /home/vsts/work/1/s/artifacts/bin/Graphics/Debug/net10.0/Microsoft.Maui.Graphics.dll
  ##vso[build.updatebuildnumber]10.0.100-ci+azdo.14634860
  Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0/Microsoft.Maui.Essentials.dll
  ##vso[build.updatebuildnumber]10.0.100-ci+azdo.14634860
  Core -> /home/vsts/work/1/s/artifacts/bin/Core/Debug/net10.0/Microsoft.Maui.dll
  ##vso[build.updatebuildnumber]10.0.100-ci+azdo.14634860
  Maps -> /home/vsts/work/1/s/artifacts/bin/Maps/Debug/net10.0/Microsoft.Maui.Maps.dll
  Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
  ##vso[build.updatebuildnumber]10.0.100-ci+azdo.14634860
  Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.Core/Debug/net10.0/Microsoft.Maui.Controls.dll
  ##vso[build.updatebuildnumber]10.0.100-ci+azdo.14634860
  ##vso[build.updatebuildnumber]10.0.100-ci+azdo.14634860
  Controls.Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.Maps/Debug/net10.0/Microsoft.Maui.Controls.Maps.dll
  Controls.Xaml -> /home/vsts/work/1/s/artifacts/bin/Controls.Xaml/Debug/net10.0/Microsoft.Maui.Controls.Xaml.dll
  TestUtils -> /home/vsts/work/1/s/artifacts/bin/TestUtils/Debug/netstandard2.0/Microsoft.Maui.TestUtils.dll
  Controls.Core.UnitTests -> /home/vsts/work/1/s/artifacts/bin/Controls.Core.UnitTests/Debug/net10.0/Microsoft.Maui.Controls.Core.UnitTests.dll
Test run for /home/vsts/work/1/s/artifacts/bin/Controls.Core.UnitTests/Debug/net10.0/Microsoft.Maui.Controls.Core.UnitTests.dll (.NETCoreApp,Version=v10.0)
VSTest version 18.0.1 (x64)

Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
[xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v2.8.2+699d445a1a (64-bit .NET 10.0.0)
[xUnit.net 00:00:00.19]   Discovering: Microsoft.Maui.Controls.Core.UnitTests
[xUnit.net 00:00:01.91]   Discovered:  Microsoft.Maui.Controls.Core.UnitTests
[xUnit.net 00:00:01.93]   Starting:    Microsoft.Maui.Controls.Core.UnitTests
[xUnit.net 00:00:02.15]   Finished:    Microsoft.Maui.Controls.Core.UnitTests
  Passed PathRenderTransformChangesStillNotifyAfterGc [53 ms]
  Passed PathDataDoesNotLeakWhenGeometryOutlivesIt(geometryType: typeof(Microsoft.Maui.Controls.Shapes.PathGeometry)) [26 ms]
  Passed PathDataDoesNotLeakWhenGeometryOutlivesIt(geometryType: typeof(Microsoft.Maui.Controls.Shapes.RectangleGeometry)) [19 ms]
  Passed PathDataDoesNotLeakWhenGeometryOutlivesIt(geometryType: typeof(Microsoft.Maui.Controls.Shapes.EllipseGeometry)) [17 ms]
  Passed PathRenderTransformDoesNotLeakWhenTransformOutlivesIt [18 ms]
  Passed PathDataReassignmentMovesChangeSubscription [2 ms]
  Passed PathRenderTransformReassignmentMovesChangeSubscription [< 1 ms]
  Passed PathDataChangesStillNotifyAfterGc [19 ms]

Test Run Successful.
Total tests: 8
     Passed: 8
 Total time: 2.7694 Seconds

📁 Fix files reverted (1 files)
  • src/Controls/src/Core/Shapes/Path.cs

📱 UI Tests — GraphicsView,Shape

Detected UI test categories: GraphicsView,Shape

Deep UI tests — 78 passed, 0 failed across 2 categories on platform-pool agent (replaces in-process counts above).

🧪 UI Test Execution Results (deep, platform pool)

Category Tests Snapshot diffs
GraphicsView 43/43 ✓
Shape 35/35 ✓
📎 Download drop-deep-uitests artifact (TRX + snapshot diffs)

📋 Pre-Flight — Context & Validation

Issue: #unknown - GitHub metadata unavailable (gh unauthenticated)
PR: #36273 - Local squashed PR branch pr-review-36273
Platforms Affected: android (requested test platform); shared Controls Shapes code affects all platforms using Path
Files Changed: 1 implementation, 1 test

Key Findings

  • PR modifies src/Controls/src/Core/Shapes/Path.cs to prevent Path from being rooted by long-lived Geometry/Transform event subscriptions.
  • PR adds src/Controls/tests/Core.UnitTests/PathMemoryTests.cs, a unit-test suite that verifies the leak fails without the fix and passes with the fix per the provided gate.
  • GitHub PR description, issue comments, review comments, and required checks could not be fetched because gh is unauthenticated in this environment; continued using local diff and existing gate artifact.

Code Review Summary

Verdict: NEEDS_DISCUSSION
Confidence: low
Errors: 0 | Warnings: 0 | Suggestions: 0

Key code review findings:

  • No concrete code defects found in local review.
  • CI/review metadata is undetermined because gh authentication is unavailable.
  • Failure-mode probes covered reassignment, Path collection while source outlives it, and PathGeometry nested invalidation.

Fix Candidates

# Source Approach Test Result Files Changed Notes
PR PR #36273 Use weak event proxy subscriptions from Path to Geometry, PathGeometry, and Transform, preserving change notifications without strongly rooting Path. ✅ PASSED (Gate) src/Controls/src/Core/Shapes/Path.cs, src/Controls/tests/Core.UnitTests/PathMemoryTests.cs Original PR

🔬 Code Review — Deep Analysis

Code Review — PR #36273

Independent Assessment

What this changes: Replaces strong Path subscriptions to Geometry.PropertyChanged, PathGeometry.InvalidatePathGeometryRequested, and Transform.PropertyChanged with weak proxy subscriptions in Path.cs, plus regression tests in PathMemoryTests.cs.

Inferred motivation: Prevent long-lived/shared Geometry or Transform instances from keeping Path instances alive.

Reconciliation with PR Narrative

Author claims: Unavailable. gh is unauthenticated and GitHub page fetch failed.
Agreement/disagreement: Available code/tests indicate a memory-leak fix for Path.Data and Path.RenderTransform. No contradiction found from local code.

Prior Review Reconciliation

Could not query the required GitHub review surfaces because gh authentication is unavailable:

  • top-level reviews: unavailable
  • inline review comments: unavailable
  • issue comments: unavailable

No prior ❌ Error findings found in available local context.

Blast Radius Assessment

  • Runs for all instances: No; only Path instances with Data or RenderTransform assigned.
  • Startup impact: No.
  • Static/shared state: No new static state.

CI Status

  • Required-check result: undetermined
  • Classification: tool/auth unavailable
  • Action taken: capped confidence at low; no comments posted. Local targeted validation passed in the pre-run gate using PathMemoryTests.

Findings

No ❌ Error, ⚠️ Warning, or 💡 Suggestion findings.

Expert reviewer produced inline-findings.json as an empty array.

Failure-Mode Probing

  • Reassignment from old to new geometry/transform: old source is unsubscribed before subscribing new source.
  • Path collected while source outlives it: weak proxy no longer roots Path; proxy self-unsubscribes on later event/finalizer path.
  • PathGeometry nested figure changes: InvalidatePathGeometryRequested remains wired through the custom weak proxy.

Verdict: NEEDS_DISCUSSION

Confidence: low
Summary: The code review found no concrete defects, and targeted tests pass in the provided gate. However, GitHub metadata, prior reviews, and required CI status could not be verified due unavailable gh authentication, so this cannot be LGTM under the skill rules.


🛠️ Fix — Analysis & Comparison

Fix Candidates

# Source Approach Test Result Files Changed Notes
1 try-fix Finalizer unsubscribes original strong event subscriptions. ❌ FAIL 1 file Finalizer cannot run while source event delegate roots Path.
2 try-fix Local per-Path callbacks capture only WeakReference. ✅ PASS 1 file Works, but duplicates WeakEventProxy behavior and can leave source-held callback delegates until source events fire. Not better than PR.
3 try-fix Remove nested Data/RenderTransform subscriptions entirely. ❌ FAIL 1 file Fixes leak class by removing subscriptions, but breaks in-place mutation notifications.
PR PR #36273 Weak proxy subscriptions from Path to Geometry, PathGeometry, and Transform. ✅ PASSED (Gate) 2 files Original PR; best balance of correctness and codebase consistency.

Cross-Pollination

Model Round New Ideas? Details
local expert loop 1 Yes Candidate 1 showed cleanup that depends on Path finalization is too late because the event source roots Path.
local expert loop 2 Yes Candidate 2 showed weak target capture passes but local duplicate weak-event code is not preferable to existing MAUI WeakEventProxy infrastructure.
local expert loop 3 No Candidate 3 showed eliminating subscriptions is too broad because nested Geometry/Transform mutation notifications are required. Remaining viable approaches collapse to weak-event subscription variants, which the PR already implements with existing framework helpers.

Exhausted: Yes
Selected Fix: PR #36273 — It is the only passing candidate that preserves nested mutation behavior while using the repository's established WeakEventProxy pattern. Candidate #2 also passes, but is not demonstrably better because it duplicates weak-event machinery locally and has less clean source-side delegate cleanup behavior.


📝 Recommended PR Title & Description

Assessment: ✏️ Recommend updating — the description is accurate and detailed, but the current title uses a nonstandard [leak-fix] prefix instead of the repository's component-focused title format.

Recommended title

Shapes: Fix Path.Data/RenderTransform memory leak

Recommended description

> [!NOTE]
> 🤖 **AI-generated PR.** This fix and its regression tests were produced by an AI-driven memory-leak workflow and verified locally. Please review carefully before merging.

Fixes #35860

### The leak

`Path.Data` and `Path.RenderTransform` subscribed the `Path` to change notifications on the assigned geometry or transform using strong handlers. A shared or long-lived value could therefore retain every `Path` using it and, through the `Path`, its visual tree or page.

### The fix

Subscribe through `WeakGeometryChangedProxy` and `WeakNotifyPropertyChangedProxy`, owned by a private, lazily-created `PathChangeSubscriptions` helper. The helper finalizer tears down the proxies when the owner is collected, preventing shared geometry and transform instances from rooting the `Path` without adding a public finalizer or changing the public API surface.

### Regression tests

`PathMemoryTests` in `src/Controls/tests/Core.UnitTests/PathMemoryTests.cs` covers:
- `PathDataDoesNotLeakWhenGeometryOutlivesIt`
- `PathRenderTransformDoesNotLeakWhenTransformOutlivesIt`
- `PathDataChangesStillNotifyAfterGc`
- `PathRenderTransformChangesStillNotifyAfterGc`
- `PathDataReassignmentMovesChangeSubscription`
- `PathRenderTransformReassignmentMovesChangeSubscription`

**Verified locally against this PR base (`main`), `Controls.Core.UnitTests`:**

| State | Result |
|---|---|
| Without fix | ❌ leak regressions fail |
| With fix | ✅ targeted `PathMemoryTests` pass |

### Scope

Managed cross-platform change for all platforms. No public API additions.

🏁 Report — Final Recommendation

Comparative Analysis — PR #36273

Candidates

Rank Candidate Regression result Assessment
1 pr ✅ Passed gate Uses existing WeakEventProxy infrastructure to prevent source-to-Path rooting while preserving nested Geometry, PathGeometry, and Transform mutation notifications.
2 pr-plus-reviewer ✅ Same as PR Expert reviewer found no actionable changes, so this is equivalent to pr with no extra diff. Ranked just below pr because it is a no-op derivative rather than a distinct improvement.
3 try-fix-2 ✅ Passed targeted tests Functionally viable, but duplicates weak-event machinery inside Path, adds more local fields/closures, and leaves cleanup behavior less aligned with existing MAUI patterns than the PR's proxy-based implementation.
4 try-fix-1 ❌ Failed regression tests Relies on a Path finalizer to unsubscribe strong event handlers, but the long-lived event source keeps Path reachable, so the finalizer cannot run and the leak remains.
5 try-fix-3 ❌ Failed regression tests Removes nested subscriptions and avoids the leak class, but breaks required in-place mutation notifications for Data and RenderTransform.

Winning candidate

Winner: pr

The raw PR fix is the best candidate because it is the only passing, non-duplicative implementation that both fixes the memory leak and preserves existing nested mutation behavior using the repository's established weak-event proxy pattern. try-fix-2 also passes the targeted tests, but it reimplements weak subscription logic locally and is not an improvement over the shared infrastructure already used by the PR. Candidates that failed regression tests are ranked below all passing candidates as required.

Notes on pr-plus-reviewer

The expert reviewer produced no actionable inline findings (inline-findings.json is []), so there was no reviewer feedback to apply in the sandbox copy. pr-plus-reviewer is therefore equivalent to pr and does not supersede it.


🧭 Next Steps — review latest findings

No alternative fix was selected for this run. Review the session findings and CI results before merging.

@MauiBot MauiBot removed the s/agent-review-in-progress AI review is currently running for this PR label Jul 12, 2026
@kubaflo

kubaflo commented Jul 12, 2026

Copy link
Copy Markdown
Contributor Author

🔍 AI-generated review follow-up (GitHub Copilot CLI, on behalf of @kubaflo)

@MauiBot The current-head review reports no errors, warnings, suggestions, inline findings, or concrete code defects. Its low-confidence verdict is only unavailable GitHub metadata and required-CI status, so no no-op code change is warranted. Ready for another review.

@kubaflo

This comment has been minimized.

@github-actions github-actions Bot added the s/agent-review-in-progress AI review is currently running for this PR label Jul 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-drawing Shapes, Borders, Shadows, Graphics, BoxView, custom drawing s/agent-fix-pr-picked AI could not beat the PR fix - PR is the best among all candidates s/agent-gate-passed AI verified tests catch the bug (fail without fix, pass with fix) s/agent-review-in-progress AI review is currently running for this PR s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Path.Data / Path.RenderTransform leaks Path visual trees when using shared app-level PathGeometry or ScaleTransform

4 participants