You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When a reference get's called from C++ code without any script environment (ie. StateBag change handlers) and they have an async return value (calling Citizen.Wait from inside said handler) the reference get's leaked/left behind.
The specific function which is the probable cause of this is this piece of code scheduler.lua#L532
Expected result
Reference should be deleted after use
Reproduction steps
Create state bag change handler with a wait inside
Invoke the change handler by changing the state bag value (doesn't matter if it's within same resource or not)
Reference get's left behind
--Reproduction code-- Get MakeFunctionReference local from schedulerlocalfunctiongetMakeFunctionReference()
locali=1whiletruedolocaln, v=debug.getupvalue(Citizen.GetFunctionReference, i)
ifnotnthenbreakendifn=="MakeFunctionReference" thenreturnvendi=i+1endendlocalfuncRefs-- Get funcRefs local from scheduler using MakeFunctionReference upvalueslocalfunctiongetFuncRefsTable()
iffuncRefs==nilthenlocalfunc=getMakeFunctionReference()
ifnotfuncthenfuncRefs=falsereturnnilendlocali=1whiletruedolocaln, v=debug.getupvalue(func, i)
ifnotnthenbreakendifn=="funcRefs" thenfuncRefs=vreturnvendi=i+1endfuncRefs=falsereturnnilendreturnfuncRefsendAddStateBagChangeHandler(nil, nil, function(bagName, key, value, reserved, replicated)
Citizen.Wait(0)
end)
Citizen.CreateThread(function()
Citizen.Wait(1000)
localfuncRefs=getFuncRefsTable()
-- Add metatable to print whenever new ref get's createdsetmetatable(funcRefs, {
__newindex=function(self, k, v)
localstackTrace=FormatStackTrace()
print(("%s: %s\n%s\n%s"):format(k, v, stackTrace, debug.traceback()))
rawset(self, k, v)
end,
})
locali=0whilei<10docollectgarbage()
localcount=0fork,vinpairs(funcRefs) docount+=1endprint("refCount", count)
GlobalState.test=math.random(-math.maxinteger, math.maxinteger)
collectgarbage()
localcount=0fork,vinpairs(funcRefs) docount+=1end-- ref count here is one higher than aboveprint("refCount", count)
i+=1Citizen.Wait(100)
endend)
Importancy
There's a workaround
Area(s)
ScRT: Lua
Specific version(s)
Server 11535 Linux
Additional information
The main issue here is that the ref counter never get's incremented, and never get's decremented, so the DeleteRefRoutine function never get's invoked.
With simple use cases where the handler isn't asynchronous the code at ResourceCallbackComponent.h#L83 handles this correctly.
The expected behavior when using async handlers is normally:
invoked resource packs the async return callback, function reference get's created
original resource unpacks the callback reference, calls DuplicateFunctionReference
GC runs, DeleteFunctionReference get's called, and reference is cleaned up.
Since this behavior is dependant on the script runtime only the first step is ran when an async handler is used anywhere where it doesn't end up in a script runtime (like said above, statebag change handlers mostly, and one other case is presentCard in the deferrals, not sure if there's more cases)
The impact with this isn't too large, but I noticed it since I was checking out some metrics on ref counts using my sketchy code above to try to diagnose some weird memory growth over time, and realized it was caused by a logger I had on state bags with a wait in it.
When there's a lot of state bag updates and the handler runs on every state bag this can add up to quite a large amount of unecessary memory usage.
The text was updated successfully, but these errors were encountered:
What happened?
When a reference get's called from C++ code without any script environment (ie. StateBag change handlers) and they have an async return value (calling Citizen.Wait from inside said handler) the reference get's leaked/left behind.
The specific function which is the probable cause of this is this piece of code scheduler.lua#L532
Expected result
Reference should be deleted after use
Reproduction steps
Importancy
There's a workaround
Area(s)
ScRT: Lua
Specific version(s)
Server 11535 Linux
Additional information
The main issue here is that the ref counter never get's incremented, and never get's decremented, so the DeleteRefRoutine function never get's invoked.
With simple use cases where the handler isn't asynchronous the code at ResourceCallbackComponent.h#L83 handles this correctly.
The expected behavior when using async handlers is normally:
Since this behavior is dependant on the script runtime only the first step is ran when an async handler is used anywhere where it doesn't end up in a script runtime (like said above, statebag change handlers mostly, and one other case is presentCard in the deferrals, not sure if there's more cases)
The impact with this isn't too large, but I noticed it since I was checking out some metrics on ref counts using my sketchy code above to try to diagnose some weird memory growth over time, and realized it was caused by a logger I had on state bags with a wait in it.
When there's a lot of state bag updates and the handler runs on every state bag this can add up to quite a large amount of unecessary memory usage.
The text was updated successfully, but these errors were encountered: