Memory paranoid question. #2

Hello. My memory paranoia doesn’t let me go.
Look:
https://github.com/garrynewman/garrysmod/blob/4497d57a7f792a4a683b37a641dee1313bfe0452/garrysmod/lua/vgui/dentityproperties.lua#L80



function PANEL:EditVariable( varname, editdata )
	....
	row.DataUpdate = function( _ )
		if ( !IsValid( self.m_Entity ) ) then self:EntityLost() return end
		row:SetValue( self.m_Entity:GetNetworkKeyValue( varname ) )
	end
	....
end


Is this always a new copy of the same function?

It always creates a new function whenever it is ran, if that’s what you’re asking.

Yes, it pisses me off, so I have to do some shit like:



local localUglyFunc = function( pnl, _ )
	if ( !IsValid( pnl.m_Entity ) ) then pnl:EntityLost() return end
	row:SetValue( pnl.m_Entity:GetNetworkKeyValue( varname ) )
end

function PANEL:EditVariable( varname, editdata )
	....
	row.DataUpdate = localUglyFunc
	....
end

// OR EVEN THIS:

function PANEL:DisgustingPrivateMetaFunctionWhichShouldNotBeHere( _ )
	if ( !IsValid( self.m_Entity ) ) then self:EntityLost() return end
	row:SetValue( self.m_Entity:GetNetworkKeyValue( varname ) )
end

function PANEL:EditVariable( varname, editdata )
	....
	row.DataUpdate = self.DisgustingPrivateMetaFunctionWhichShouldNotBeHere
	....
end



:frowning:

Ohhh boy, I love these threads!

Disclaimer that I probably know what I’m talking about but I can’t be bothered to search through the LuaJIT source this time.

Lua will create a new closure with different references to upvalues, but it will reference the bytecode from the original prototype. It won’t make a new copy of the code or do any crazy shit like trying to recompile it.

So: Sort of, but it isn’t a big deal.

Don’t be afraid of using closures. They are lovely.:joy:

Disclaimer that this is really not worth worrying about, memory is basically your cheapest resource, and this looks to be an O(N) situation with a tiny constant, even if I’m wrong about the prototype.

Also remember that Lua has a GC, so older and now useless copies of the closure will get disposed of eventually, so it’s not like there’s a large memory leak or something.

GC makes no sense there cuz these funcs will live together with addon all time, they aren’t temporary.

Anyway, MadParakeet seems like calm me a bit, so I can continue to code for a while, till another scary thing.

I’ve said closures and not functions. A closure isn’t a function, it’s a pointer to function’s bytecode and a table of its captured upvalues. These do get garbage collected when they’re not referenced anymore.

As I said… They will referenced for entire addon’s life because the GUI element appears on the screen when you join the server and destroys when you leave… So it won’t be destroyed early.

Wtf no. When you call the :EditVariable() function the row.DataUpdate gets reassigned, which means that the old closure gets thrown away and will be GC’d eventually.

EDIT: Okay, maybe you just didn’t get what’s being allocated here. The function body is static but it exists for as long as the lua_State exist; the closure, however, is created when the row.DataUpdate = function() … end bit is executed. It does not copy the bytecode anywhere, it just allocates a structure where it puts a reference to it, and also (in this case) references for three upvalues, self, row and varname. This structure is GC-able and will live until row.DataUpdate field is reassigned again and the GC is triggered.

You got me wrong, I know how GC works.
I meant that there will be a number of these GUIs and each of them (Ofc older GCed) will have its function, each different at the same time:



local PANEL = {}
function PANEL:EditVariable( varname, editdata )
	....
	row.DataUpdate = function( _ )
		if ( !IsValid( self.m_Entity ) ) then self:EntityLost() return end
		row:SetValue( self.m_Entity:GetNetworkKeyValue( varname ) )
	end
	....
end
derma.DefineControl("MySillyVgui", "", PANEL, "Panel")

for i = 1, 5 do
	vgui.Create("MySillyVgui") // 5 identical functions for each of VGUI.
end

/////////////////////

local localFunc = function(pnl, _ )
	if ( !IsValid( pnl.m_Entity ) ) then pnl:EntityLost() return end
	row:SetValue( pnl.m_Entity:GetNetworkKeyValue( varname ) )
end
local PANEL = {}
function PANEL:EditVariable( varname, editdata )
	....
	row.DataUpdate = localFunc
	....
end
derma.DefineControl("MySillyVgui2", "", PANEL, "Panel")

for i = 1, 5 do
	vgui.Create("MySillyVgui2") // 5 pointers to the same function
end



If you know how this stuff works (and no, you don’t), then why are you asking these questions?

No no no no no. Read the edit to my post, the bytecode for the function is never copied.

Alright, now it seems a bit more understandably :slight_smile: Thanks