-- ------------------------------------------------------------------------------ -- -- 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, "") end return context.numCreated, totalCount, debugInfo end