lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]

Hi there,

I came across a supposed leak[1,2] with some C code. I think I distilled
the behaviour that causes problems into the attached Lua program and
would now like to get some input on "which code is to blame".

I noticed that the problem occurs with Lua 5.3, but does not occur with
Lua 5.1 or 5.2. Thus, I could do a Git bisect based on the GitHub mirror
of the Lua source code[3]. The result is that commit [4] introduced the

My main question now is: What to do about this?

Some more information about the program: The library in question
provides bindings to GObject-based C libraries. To support callbacks, it
has to luaL_ref() the callback function and it will then luaL_unref()
later from the __gc metamethod of some of the involved object.

This is just what the attached program does: In a tight loop, it creates
a table, luaL_ref()s it and arranges for a call to luaL_unref() later.
Apparently the code can now create references faster than they are released.

I hope this is enough information to help me. Thanks for any hints and
suggestions that you might have!


Bruce Schneier can read and understand Perl programs.
local registry = {}
local refnil = -1

-- Lua-based reimplementations of luaL_ref and luaL_unref. Yes, this makes no
-- sense to have in Lua, but helps with the following experiments.
local function luaL_ref(t, obj)
	if not obj then
		return refnil
	local next_free = t[0]
	local ref
	if next_free then
		ref = next_free
		t[0] = t[next_free]
		ref = #t + 1
	t[ref] = obj
	return ref
local function luaL_unref(t, ref)
	if ref >= 0 then
		-- Push the free reference into the freelist
		t[0], t[ref] = ref, t[0]

-- Some helper functions
local guard_mt = {
	__gc = function(self)
		self.callback(self.arg1, self.arg2)
local function make_guard(callback, arg1, arg2)
	setmetatable({callback = callback, arg1 = arg1, arg2 = arg2}, guard_mt)

-- Now simulate what LGI is doing when running the "leaky program" (
local iter = 0
local stop = 1000
local max = 0
for i=1, stop do
	--print(iter, collectgarbage("count"), #registry, registry[0])
	max = math.max(max, collectgarbage("count"))
	if max > 50000 then
		error("too much memory used after " .. tostring(i/stop*100))
	for i = 1, 1000 do
		local ref = luaL_ref(registry, {})
		make_guard(luaL_unref, registry, ref)
		iter = iter + 1