mirror of
https://github.com/brues-code/Vanilla_MikScrollingBattleText.git
synced 2026-10-30 08:30:49 +00:00
Replace MikTRO with a ClassicAPI object pool
Animation event tables now come from CreateUnsecuredObjectPool and are wiped on release, keeping garbage down under 1.12's stop-the-world GC. Release the tables that were previously dropped for the GC (hidden events, the power-gain early return), and make /msbt stats report the number of tables in use as a leak check.
This commit is contained in:
@@ -314,9 +314,9 @@ local stillMerging = false;
|
||||
local activeNonStickies = {};
|
||||
local activeStickies = {};
|
||||
|
||||
-- Create a new table recycler object that holds up to 40 table references
|
||||
-- in primary cache.
|
||||
local eventsRecycler = MikTRO:NewRecyclerObject(40);
|
||||
-- Pool of animation event tables, reused to keep garbage (and 1.12's full-heap GC pauses) down.
|
||||
-- Every acquired table must be released exactly once.
|
||||
local eventsPool = CreateUnsecuredObjectPool(function() return {} end, function(_, t) table.wipe(t) end);
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------------
|
||||
@@ -589,8 +589,8 @@ end
|
||||
-- Receive combat events from Mik's combat event helper.
|
||||
-- **********************************************************************************
|
||||
function MikSBT.CombatEventsHandler(combatEvent)
|
||||
-- Get a recycled table from the events recycler object.
|
||||
local animationEvent = eventsRecycler:AcquireTable();
|
||||
-- Get a table from the events pool.
|
||||
local animationEvent = eventsPool:Acquire();
|
||||
|
||||
-- Look up the scroll area and event type string via pre-built tables (no string concatenation).
|
||||
local dirType = combatEvent.DirectionType;
|
||||
@@ -712,9 +712,9 @@ function MikSBT.CombatEventsHandler(combatEvent)
|
||||
-- Dispatch the event to the animation system.
|
||||
MikSBT.DispatchAnimationEvent(animationEvent);
|
||||
else
|
||||
-- Print an error message and reclaim the animation event table to the table recycler.
|
||||
-- Print an error message and release the animation event table to the events pool.
|
||||
MikSBT.Print("No profile data found for " .. eventTypeString, 1, 0, 0)
|
||||
eventsRecycler:ReclaimTable(animationEvent);
|
||||
eventsPool:Release(animationEvent);
|
||||
end
|
||||
end
|
||||
|
||||
@@ -724,15 +724,15 @@ end
|
||||
-- Receive trigger events from Mik's trigger helper.
|
||||
-- **********************************************************************************
|
||||
function MikSBT.TriggerHandler(triggerEvent)
|
||||
-- Get a recycled table from the events recycler object.
|
||||
local animationEvent = eventsRecycler:AcquireTable();
|
||||
-- Get a table from the events pool.
|
||||
local animationEvent = eventsPool:Acquire();
|
||||
|
||||
-- Check if the trigger require a stance
|
||||
if (MikSBT.CurrentProfile.Triggers[triggerEvent.TriggerKey].TriggerSettings.Stance) then
|
||||
local _,_,isActive = GetShapeshiftFormInfo(MikSBT.CurrentProfile.Triggers[triggerEvent.TriggerKey].TriggerSettings.Stance)
|
||||
if not isActive then
|
||||
-- Reclaim the animation event table to the table recycler.
|
||||
eventsRecycler:ReclaimTable(animationEvent);
|
||||
-- Release the animation event table to the events pool.
|
||||
eventsPool:Release(animationEvent);
|
||||
return
|
||||
end
|
||||
end
|
||||
@@ -751,9 +751,9 @@ function MikSBT.TriggerHandler(triggerEvent)
|
||||
-- Dispatch the event to the animation system.
|
||||
MikSBT.DispatchAnimationEvent(animationEvent);
|
||||
else
|
||||
-- Print an error message and reclaim the animation event table to the table recycler.
|
||||
-- Print an error message and release the animation event table to the events pool.
|
||||
MikSBT.Print("No profile data found for trigger key " .. triggerEvent.TriggerKey, 1, 0, 0);
|
||||
eventsRecycler:ReclaimTable(animationEvent);
|
||||
eventsPool:Release(animationEvent);
|
||||
end
|
||||
end
|
||||
|
||||
@@ -913,8 +913,8 @@ function MikSBT.MergeEvents(mergeData, numEvents)
|
||||
for x = 1, numEvents do
|
||||
-- Check if the event was merged into a different animation event.
|
||||
if (mergeData.UnmergedEvents[x] and mergeData.UnmergedEvents[x].EventMerged) then
|
||||
-- Reclaim the animation event table to the events recycler.
|
||||
eventsRecycler:ReclaimTable(mergeData.UnmergedEvents[x]);
|
||||
-- Release the animation event table to the events pool.
|
||||
eventsPool:Release(mergeData.UnmergedEvents[x]);
|
||||
end
|
||||
|
||||
mergeData.UnmergedEvents[x] = nil;
|
||||
@@ -1692,6 +1692,9 @@ function MikSBT.DispatchAnimationEvent(animationEvent)
|
||||
else
|
||||
MikSBT.AddAnimation(animationEvent);
|
||||
end
|
||||
else
|
||||
-- The event is hidden so release it.
|
||||
eventsPool:Release(animationEvent);
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1707,17 +1710,18 @@ function MikSBT.AddAnimation(animationEvent)
|
||||
for _, suppressionSettings in MikSBT.CurrentProfile.Suppressions do
|
||||
-- Check if the suppression's search pattern is a match.
|
||||
if (suppressionSettings.Enabled and (string_find(animationEvent.Text, suppressionSettings.SearchPattern))) then
|
||||
-- Reclaim the animation event table to the events recycler and leave the function.
|
||||
eventsRecycler:ReclaimTable(animationEvent);
|
||||
-- Release the animation event table to the events pool and leave the function.
|
||||
eventsPool:Release(animationEvent);
|
||||
return;
|
||||
elseif (suppressionSettings.Enabled and animationEvent.EffectName and string_find(animationEvent.EffectName, suppressionSettings.SearchPattern)) then
|
||||
-- Reclaim the animation event table to the events recycler and leave the function.
|
||||
eventsRecycler:ReclaimTable(animationEvent);
|
||||
-- Release the animation event table to the events pool and leave the function.
|
||||
eventsPool:Release(animationEvent);
|
||||
return;
|
||||
end
|
||||
end
|
||||
|
||||
if animationEvent.EventType == "MSBT_EVENTTYPE_NOTIFICATION_POWER_GAIN" and animationEvent.EffectName ~= 0 and MikSBT.CurrentProfile.ShowAllManaGains then
|
||||
eventsPool:Release(animationEvent);
|
||||
return
|
||||
end
|
||||
|
||||
@@ -1896,8 +1900,8 @@ function MikSBT.AddAnimation(animationEvent)
|
||||
MikSBT.RepositionAnimDisplayInfo(animationEvent.ScrollArea);
|
||||
end
|
||||
|
||||
-- Reclaim the animation event table to the events recycler.
|
||||
eventsRecycler:ReclaimTable(animationEvent);
|
||||
-- Release the animation event table to the events pool.
|
||||
eventsPool:Release(animationEvent);
|
||||
end
|
||||
|
||||
-- **********************************************************************************
|
||||
@@ -2682,8 +2686,8 @@ function MikSBT.CommandHandler(params)
|
||||
showUsage = false;
|
||||
|
||||
elseif (currentParam == MikSBT.COMMAND_STATS) then
|
||||
-- Print out stats about the event recycler.
|
||||
eventsRecycler:PrintStats();
|
||||
-- Print the number of event tables in use; a count that keeps growing while idle means a missed release.
|
||||
MikSBT.Print("Event tables in use: " .. eventsPool:GetNumActive(), 1, 1, 1);
|
||||
|
||||
-- Don't show the usage info.
|
||||
showUsage = false;
|
||||
@@ -2816,8 +2820,8 @@ function MikSBT.DisplayMessage(message, displayType, isSticky, colorR, colorG, c
|
||||
end
|
||||
|
||||
|
||||
-- Get a recycled table from the events recycler object.
|
||||
local animationEvent = eventsRecycler:AcquireTable();
|
||||
-- Get a table from the events pool.
|
||||
local animationEvent = eventsPool:Acquire();
|
||||
|
||||
-- Populate the animation event data.
|
||||
animationEvent.ScrollArea = scrollArea;
|
||||
|
||||
@@ -16,7 +16,6 @@ MikScrollingBattleText-deDE.lua
|
||||
MikScrollingBattleText-ruRU.lua
|
||||
|
||||
localization.lua
|
||||
MikTableRecyclerObject.lua
|
||||
MikCombatEventHelper.lua
|
||||
MikScrollingBattleText.lua
|
||||
MikCombatEventHelper.xml
|
||||
|
||||
@@ -1,168 +0,0 @@
|
||||
-------------------------------------------------------------------------------------
|
||||
-- Title: Mik's Table Recyler Object
|
||||
-- Author: Mik
|
||||
-- Maintainer: Athene
|
||||
-- Credits:
|
||||
-- Thanks to tekkub, the author of compostLib. I adapted much of his code for
|
||||
-- this object.
|
||||
-------------------------------------------------------------------------------------
|
||||
|
||||
-- Create "namespace."
|
||||
MikTRO = {};
|
||||
|
||||
-- Set the __index property to the recycler object namespace so
|
||||
-- when a new recycler object sets it metatable to the namespace
|
||||
-- the recycler object inherits the functions in the namespace.
|
||||
MikTRO.__index = MikTRO;
|
||||
|
||||
|
||||
-- **********************************************************************************
|
||||
-- Creates a recycler object.
|
||||
-- **********************************************************************************
|
||||
function MikTRO:NewRecyclerObject(maxEntries)
|
||||
-- Create a table to use.
|
||||
recyclerObject = {};
|
||||
|
||||
-- Create a table to hold the cache entries and set its max permanent size.
|
||||
recyclerObject.PrimaryCache = {};
|
||||
recyclerObject.PrimaryCacheSize = 0;
|
||||
recyclerObject.MaxEntries = maxEntries;
|
||||
|
||||
-- Create some variables to hold stats.
|
||||
recyclerObject.NumNew = 0;
|
||||
recyclerObject.NumRecycled = 0;
|
||||
recyclerObject.NumErased = 0;
|
||||
recyclerObject.NumReclaimed = 0;
|
||||
recyclerObject.AmountMemFreed = 0;
|
||||
|
||||
|
||||
-- Create a table to hold overflow entries. If more tables are reclaimed
|
||||
-- than the size of the max primary cache can hold, they will be reclaimed
|
||||
-- into the overflow cache which is a weak table. This allows for spikes
|
||||
-- in cache usage to still be optimized while not preventing garbage
|
||||
-- collection on the extra data once there are no other references to them.
|
||||
recyclerObject.OverflowCache = {};
|
||||
setmetatable(recyclerObject.OverflowCache, {__mode = "v"});
|
||||
|
||||
|
||||
-- Set the meta table for the cache object to the table recycler
|
||||
-- obect's namespace so the cache object inherits the functions of
|
||||
-- table recycler object.
|
||||
setmetatable(recyclerObject, self);
|
||||
|
||||
-- Return the recycler object.
|
||||
return recyclerObject;
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- **********************************************************************************
|
||||
-- Removes a table from the cache and returns it. If there are no tables available
|
||||
-- in the cache a new one is returned.
|
||||
-- **********************************************************************************
|
||||
function MikTRO:AcquireTable()
|
||||
-- Check if there are any tables available in the primary cache and if so
|
||||
-- return one.
|
||||
if (self.PrimaryCacheSize > 0) then
|
||||
-- Increment the number of recycled tables.
|
||||
self.NumRecycled = self.NumRecycled + 1;
|
||||
|
||||
self.PrimaryCacheSize = self.PrimaryCacheSize - 1;
|
||||
return table.remove(self.PrimaryCache);
|
||||
|
||||
-- Check if there are any tables available in the overflow cache and if so
|
||||
-- return one.
|
||||
elseif self:TablesInOverflow() then
|
||||
-- Increment the number of recycled tables.
|
||||
self.NumRecycled = self.NumRecycled + 1;
|
||||
|
||||
-- Loop through the tables in the overflow and return one.
|
||||
for i in pairs(self.OverflowCache) do
|
||||
return table.remove(self.OverflowCache, i);
|
||||
end
|
||||
|
||||
-- No tables available so return a new one.
|
||||
else
|
||||
-- Increment the number of new tables created.
|
||||
self.NumNew = self.NumNew + 1;
|
||||
return {};
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- **********************************************************************************
|
||||
-- Reclaims a table into the cache.
|
||||
-- **********************************************************************************
|
||||
function MikTRO:ReclaimTable(t)
|
||||
-- Check if the passed value isn't valid.
|
||||
if (type(t) ~= "table") then
|
||||
return;
|
||||
end
|
||||
|
||||
-- Erase the passed table.
|
||||
self:EraseTable(t);
|
||||
|
||||
-- Check if the primary cache is already full.
|
||||
if (self.PrimaryCacheSize >= self.MaxEntries) then
|
||||
-- Insert the table into the overflow cache.
|
||||
table.insert(self.OverflowCache, t);
|
||||
else
|
||||
-- Insert the table into the primary cache.
|
||||
table.insert(self.PrimaryCache, t);
|
||||
self.PrimaryCacheSize = self.PrimaryCacheSize + 1;
|
||||
end
|
||||
|
||||
-- Increment the number of reclaimed tables.
|
||||
self.NumReclaimed = self.NumReclaimed + 1;
|
||||
end
|
||||
|
||||
|
||||
-- **********************************************************************************
|
||||
-- Erases the passed table. Subtables are NOT erased.
|
||||
-- **********************************************************************************
|
||||
function MikTRO:EraseTable(t)
|
||||
-- Check if the passed value isn't valid.
|
||||
if (type(t) ~= "table") then
|
||||
return;
|
||||
end
|
||||
|
||||
-- Loop through all the keys in the table and clear it.
|
||||
for key in pairs(t) do
|
||||
t[key] = nil;
|
||||
end
|
||||
|
||||
-- Set the length of the table to 0.
|
||||
table.setn(t, 0);
|
||||
|
||||
-- Increment the number of tables erased.
|
||||
self.NumErased = self.NumErased + 1;
|
||||
end
|
||||
|
||||
|
||||
-- **********************************************************************************
|
||||
-- Prints stats.
|
||||
-- **********************************************************************************
|
||||
function MikTRO:PrintStats()
|
||||
local overflowSize = 0;
|
||||
for i in pairs(self.OverflowCache) do
|
||||
overflowSize = overflowSize + 1;
|
||||
end
|
||||
|
||||
DEFAULT_CHAT_FRAME:AddMessage(string.format(
|
||||
"|cff00ff00New Tables: %d|r |cffffff00Recycled Tables: %d|r |cff00ffffCached Tables: %d|r |cffff0000Overflow Tables: %d|r |cff888888Erased Tables: %d|r |cffff00ffMemory Saved: %d KiB|r |cffff0088Tables Lost to GC: %d",
|
||||
self.NumNew,
|
||||
self.NumRecycled,
|
||||
self.PrimaryCacheSize,
|
||||
overflowSize,
|
||||
self.NumErased,
|
||||
self.AmountMemFreed + ((32/1024) * self.NumRecycled),
|
||||
self.NumReclaimed - self.NumRecycled - self.PrimaryCacheSize));
|
||||
end
|
||||
|
||||
|
||||
-- **********************************************************************************
|
||||
-- Checks if there are any tables in the overflow cache.
|
||||
-- **********************************************************************************
|
||||
function MikTRO:TablesInOverflow()
|
||||
return next(self.OverflowCache) ~= nil;
|
||||
end
|
||||
@@ -48,7 +48,7 @@ MikSBT.COMMAND_USAGE = {
|
||||
" " .. MikSBT.COMMAND_DISABLE .. " - Disables the mod.",
|
||||
" " .. MikSBT.COMMAND_ENABLE .. " - Enables the mod.",
|
||||
" " .. MikSBT.COMMAND_SHOWVER .. " - Shows the current version.",
|
||||
" " .. MikSBT.COMMAND_STATS .. " - Reports stats about table recycling.",
|
||||
" " .. MikSBT.COMMAND_STATS .. " - Reports how many pooled event tables are in use.",
|
||||
" " .. MikSBT.COMMAND_SEARCH .. " filter - Sets a filter for searching event types.",
|
||||
" " .. MikSBT.COMMAND_DEBUG .. " - Toggles debug mode.",
|
||||
" " .. MikSBT.COMMAND_HELP .. " - Show the command usage.",
|
||||
|
||||
@@ -59,7 +59,7 @@ Interface/
|
||||
| `/msbt disable` | Disable the addon |
|
||||
| `/msbt enable` | Enable the addon |
|
||||
| `/msbt version` | Show the current version |
|
||||
| `/msbt stats` | Report table recycling statistics |
|
||||
| `/msbt stats` | Report how many pooled event tables are in use |
|
||||
| `/msbt search <filter>` | Set a filter for searching event types |
|
||||
| `/msbt debug` | Toggle debug mode |
|
||||
| `/msbt help` | Show command usage |
|
||||
@@ -87,7 +87,7 @@ SuperWoW, UnitXP SP3 and Nampower integration has been removed in favor of Class
|
||||
- **Dispatch table**: Combat event routing uses O(1) table lookup instead of an if/elseif chain
|
||||
- **SpellId icon cache**: SpellIds from combat log events resolve icons directly through ClassicAPI and are cached, bypassing Babble-Spell dictionary lookups and tooltip scanning
|
||||
- **Event throttling**: UNIT_HEALTH and UNIT_MANA trigger checks are throttled to 150ms intervals
|
||||
- **Table recycling**: `MikTRO` (Table Recycler Object) pools and reuses table allocations to reduce garbage collection pressure
|
||||
- **Table pooling**: Animation event tables come from a ClassicAPI object pool (`CreateUnsecuredObjectPool`) and are reused, so 1.12's stop-the-world GC runs less often
|
||||
|
||||
## Architecture
|
||||
|
||||
@@ -97,7 +97,6 @@ SuperWoW, UnitXP SP3 and Nampower integration has been removed in favor of Class
|
||||
|---|---|
|
||||
| `MikSBT` | Main addon: display, animation, profiles, event routing, slash commands |
|
||||
| `MikCEH` | Combat Event Helper: parses combat events into structured data |
|
||||
| `MikTRO` | Table Recycler Object: memory pool for table reuse |
|
||||
|
||||
### Event Processing Pipeline
|
||||
|
||||
|
||||
Reference in New Issue
Block a user