TradeSkillMaster/LibTSM/Util/ObjectPool.lua

121 lines
3.6 KiB
Lua
Raw Permalink Normal View History

2020-11-13 14:13:12 -05:00
-- ------------------------------------------------------------------------------ --
-- TradeSkillMaster --
-- https://tradeskillmaster.com --
-- All Rights Reserved - Detailed license information included with addon. --
-- ------------------------------------------------------------------------------ --
--- ObjectPool Functions.
-- @module ObjectPool
local _, TSM = ...
local ObjectPool = TSM.Init("Util.ObjectPool")
local Debug = TSM.Include("Util.Debug")
local private = {
debugLeaks = TSM.__IS_TEST_ENV or false,
instances = {},
context = {},
}
local DEBUG_STATS_MIN_COUNT = 1
-- ============================================================================
-- Metatable
-- ============================================================================
local OBJECT_POOL_MT = {
__index = {
Get = function(self)
local context = private.context[self]
local obj = tremove(context.freeList)
if not obj then
context.numCreated = context.numCreated + 1
obj = context.createFunc()
assert(obj)
end
if private.debugLeaks then
context.state[obj] = (Debug.GetStackLevelLocation(2 + context.extraStackOffset) or "?").." -> "..(Debug.GetStackLevelLocation(3 + context.extraStackOffset) or "?")
else
context.state[obj] = "???"
end
return obj
end,
Recycle = function(self, obj)
local context = private.context[self]
assert(context.state[obj])
context.state[obj] = nil
tinsert(context.freeList, obj)
end,
},
__newindex = function(self, key, value) error("Object pool cannot be modified") end,
__metatable = false,
}
-- ============================================================================
-- Module Functions
-- ============================================================================
--- Create a new object pool.
-- @tparam string name The name of the object pool for debug purposes
-- @tparam function createFunc The function which is called to create a new object
-- @tparam[opt=0] number extraStackOffset The extra stack offset for tracking where objects are being used from or nil to disable stack info
-- @treturn ObjectPool The object pool object
function ObjectPool.New(name, createFunc, extraStackOffset)
assert(createFunc)
assert(not private.instances[name])
local pool = setmetatable({}, OBJECT_POOL_MT)
private.context[pool] = {
createFunc = createFunc,
extraStackOffset = extraStackOffset or 0,
freeList = {},
state = {},
numCreated = 0,
}
private.instances[name] = private.context[pool]
return pool
end
function ObjectPool.EnableLeakDebug()
private.debugLeaks = true
end
function ObjectPool.GetDebugInfo()
local debugInfo = {}
for name, context in pairs(private.instances) do
local numCreated, numInUse, info = private.GetDebugStats(context)
debugInfo[name] = {
numCreated = numCreated,
numInUse = numInUse,
info = info,
}
end
return debugInfo
end
-- ============================================================================
-- Private Helper Functions
-- ============================================================================
function private.GetDebugStats(context)
local counts = {}
local totalCount = 0
for _, caller in pairs(context.state) do
counts[caller] = (counts[caller] or 0) + 1
totalCount = totalCount + 1
end
local debugInfo = {}
for info, count in pairs(counts) do
if count > DEBUG_STATS_MIN_COUNT then
tinsert(debugInfo, format("[%d] %s", count, info))
end
end
if #debugInfo == 0 then
tinsert(debugInfo, "<none>")
end
return context.numCreated, totalCount, debugInfo
end