Repository navigation
Fix memory leak when element becomes inactive after src swap - #620
guilhermesimoes wants to merge 2 commits into
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
The new ordering can corrupt texture active counts during re-entrant txUnloaded callbacks.
1 open finding
What changed in this PR
Fixes texture-source retention when an element becomes inactive after swapping textures.
Changes:
- Reorders active-state handling during texture deactivation.
- Adds regression coverage for garbage-collecting the previous source.
| File | Description |
|---|---|
src/tree/Element.mjs |
Adjusts texture deactivation ordering. |
tests/textures/test.textures.js |
Tests cleanup after a texture swap. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| @@ -406,12 +406,13 @@ export default class Element { | |||
| if (this.__texture) { | |||
| this.__texture.decActiveCount(); | |||
There was a problem hiding this comment.
@guilhermesimoes the decActiveCount is called here? so im not sure this is actually a fix..
There was a problem hiding this comment.
But the test that I added fails without the current change in Element 🤔 but you're right, let me take a look
There was a problem hiding this comment.
Ah, we're dealing with different textures! This leak only occurs when the element is swapping srcs / textures. I've updated the PR which hopefully should make the issue clearer.
f4fcb62 to
7fd7485
Compare
7fd7485 to
afc6819
Compare
afc6819 to
7fd7485
Compare

When an element swaps
srcs, there's a brief moment in time where it has 2 different textures:this.__texture(new) andthis.__displayedTexture(old). When the element becomes inactive, the hook_unsetActiveFlaggets called which does this:Lightning/src/tree/Element.mjs
Lines 406 to 408 in 6ca16cd
But it forgets to do the same for the old (but still displayed) texture! Since the old texture's usage count is not properly decremented, the old
textureand itssourceappear to still be in use, and they get stuck forever in memory. Callingstage.gc()still won't release them, because thesource'sisUsedwill always returntrueandallowCleanupwill always returnfalse.