Skip to content

#2014 Pool NativeUI widgets instead of abandoning their frames - #2017

Merged
TimothyLuke merged 1 commit into
TimothyLuke:masterfrom
LarryThiessen:fix-2014-widget-pool
Aug 24, 2026
Merged

#2014 Pool NativeUI widgets instead of abandoning their frames#2017
TimothyLuke merged 1 commit into
TimothyLuke:masterfrom
LarryThiessen:fix-2014-widget-pool

Conversation

@LarryThiessen

Copy link
Copy Markdown
Contributor

Fixes #2014

Release() only hid a widget's frame — and WoW frames can never be destroyed — so every editor redraw abandoned its entire widget set: measured +238 widgets per version click, permanently. Long sessions accumulate thousands of dead frames and megabytes of unreclaimable state. AceGUI pooled widgets for exactly this reason; the NativeUI rewrite dropped it.

Widgets are plain tables of closures over their own frame, so the same table is reused: UI:Create hands out a banked widget when one exists; Release banks poolable types. The reset contract — every part of which covers a real bug found during soak testing:

  • every key added after construction is removed (caller decorations, wrapped methods);
  • callbacks/children become fresh tables;
  • frame scripts recorded at construction are restored on every subframe the widget exposes;
  • regions and child frames callers created directly on the widget's frame are hidden on reuse — the key sweep cannot see those; without this a reused frame keeps rendering its previous life (header rows bleeding into macro blocks, doubled character counters, dead scrollbars intercepting the mouse wheel);
  • a double Release cannot bank the same widget twice.

Only the 13 high-churn leaf/container types pool; windows, scroll frames, tab groups and the tree keep their old behaviour. Diagnostics kill switch: /run GSE_NoWidgetPool = true + /reload.

Best merged together with #2013 — pooled reuse of macro edit boxes is what makes the scrollbar re-show race fixed there common.

Testing

  • luac -p clean.
  • Confirmed in a running client: >95% pool reuse once warm, widget creation per click ~0, session memory stops climbing; soak-tested through normal editing (blocks, Tab builder, icons, checkboxes, dropdowns, config pages) with the state-bleed bugs above found and fixed during that soak.

🤖 Generated with Claude Code

…ames

Release() only hid a widget's frame -- and WoW frames can never be
destroyed -- so every editor redraw abandoned its entire widget set:
+238 widgets per version click, forever. Long sessions accumulated
thousands of dead frames and megabytes of unreclaimable state, and the
collector dragged an ever-growing graph for the rest of the session.
AceGUI pooled widgets for exactly this reason; the rewrite dropped it.

Widgets are plain tables of closures over their own frame, so the SAME
table is reused. UI:Create hands out a banked widget when one exists;
Release banks poolable types. The reset contract, each part of which
covers a real bug found in testing:

  * every key added after construction is removed (caller decorations
    and wrapped methods must not survive into the next life)
  * callbacks/children become fresh tables
  * frame scripts recorded at construction are restored on every
    subframe the widget exposes
  * regions and child frames callers created directly on the widget's
    frame are hidden on reuse -- the key sweep cannot see those, and
    without this a reused frame keeps rendering its previous life
    (header rows bleeding into macro blocks, doubled counters, dead
    scrollbars intercepting the mouse wheel)
  * a double Release cannot bank the same widget twice

Only the 13 high-churn leaf/container types pool; windows, scroll
frames, tab groups and the tree keep their old behaviour. Kill switch
for diagnostics: /run GSE_NoWidgetPool = true and /reload.

Best merged with TimothyLuke#2013: pooled reuse of macro edit boxes is what makes
the scrollbar re-show race there common.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@TimothyLuke
TimothyLuke merged commit 354682f into TimothyLuke:master Aug 24, 2026
1 check passed
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.

NativeUI leaks every widget: Release() only hides frames, so each editor redraw abandons its whole widget set

2 participants