why __gc didn't work when collecting garbage for table? - lua

Recently, I was learning about garbage collection. I have coded an example. The code is as follows:
mytablea {"1","2","3"}
print(collectgarbage("count"))
for i = 1, 500000 do
table.insert(mytable, i)
end
debug.setmetatable(mytable, {_gc = function ()print("dead")end})
mytable = nil
print(collectgarbage("count"))
print(collectgarbage("collect"))
print(collectgarbage("count"))
I'm confused about why there is no output in my terminal (__gc doesn't seem to work). But after finishing the call collectgarbage, its memory does decrease! I wonder if lua 5.1 didn't support this on table or if something is wrong with my linux (maybe some bug in my ubuntu).

The correct metatable entry is __gc, not _gc.
In any case, Lua 5.1 does not support gc metamethods for tables.

Lua 5.1 does not support __gc (note, it's two underscores, not one) on tables.
If possible move to a newer version of Lua (i.e. 5.2 or 5.3) to get support.
If you really need it; you can use the undocumented newproxy function to get a userdata to attach a __gc to instead:
do
local p = newproxy(false);
debug.setmetatable(p, {__gc = function() print("dead") end})
local t = {some_key = p}
end
collectgarbage()
collectgarbage()
I use this in projects where I need lua 5.1 compatibility, e.g. https://github.com/wahern/cqueues/blob/3f2fc57a07bb9e658f4d53ccc60ba8177e3f1236/src/dns.resolvers.lua#L53

Related

Lua 5.2: Upvalue persistence in dumped functions, problematic due to _ENV upvalue shifting indexes

I know functions with upvalues aren't officially supported to be dumped and persisted, but it works. And in Lua versions above 5.2, you can manually set each upvalue with debug.setupvalue and the resultant function will work fine. I've been experimenting with this for a little while and I cannot expand Lua 5.2 support because the _ENV upvalue needs to be manually inserted as the first upvalue, which disrupts the hardcoded indexes of the other upvalues inside the bytecode.
Here's a working example of this problem:
local u1, u2, u3 = 1, 2, 3
local function upvalues(fn, upvals)
local setupvalue = (debug or {}).setupvalue
if setupvalue then
setupvalue(fn, 1, _ENV)
for key, value in ipairs(upvals) do
setupvalue(fn, key + 1, value)
end
return fn
else
return fn
end
end
local dump = load(string.dump(function() return u1, u2, u3 end))
dump = upvalues(dump, { 1, 2, 3 })
print(dump())
We expect 1, 2, 3 return, but we get table, 2, 3 because the first upvalue index has been changed to _ENV instead of the number 1.
I want to know if there's a way to work with or around this behavior in Lua 5.2. The debug.upvaluejoin looks interesting to me but I am very unfamiliar with it and haven't been able to get a working example of it for a while. I mean, even differences from upvalues function implementation to manually modifying the textual bytecode is something I'd like to learn about.
EDIT: It's not clear in my original post that I receive these functions dynamically, so I can't quite edit their content during runtime.

Lua: has the behaviour of garbage collector changed with Lua 5.3.2?

I'm facing a problem of memory leak in Lua. To understand how the gc works I have written a small script and the results are surprising (for me):
local mt = { __gc = function() end }
local function new()
local o = { s = string.rep(os.date("%c"), 500) }
setmetatable(o, mt)
end
local before = math.floor(collectgarbage("count"))
for i = 1, 100000 do new() end
local after = math.floor(collectgarbage("count"))
print(before, after)
I'm creating 100000 objects, each objet has a metatable having a __gc function.
As result I get :
with Lua 5.2.4 : 25 130 (25 kb of memory at start, 130 kb at the
end of the script)
with Lua 5.3.5 : 25 17470
Lua 5.3.5 does not garbage the objects as Lua 5.2.4 did.
I used the same script on different Lua versions (5.3.0, 5.3.1, 5.3.2, 5.3.3, 5.3.5) and this behaviour appears starting with Lua 5.3.2.
More surprising is that, if I remove the __gc function, the results are quite the same for all Lua versions.
Do you think that I'm missing something or is it a bug introduced in Lua 5.3.2 ?
Edit : this script behaves with Lua 5.4.0 as with Lua 5.3.1. So the bug is solved.

local variable cannot be seen in a closure across the file?

Suppose I have the following two Lua files:
In a.lua:
local x = 5
f = dofile'b.lua'
f()
In b.lua:
local fun = function()
print(x)
end
return fun
Then if I run luajit a.lua in shell it prints nil since x cannot be seen in the function defined in b.lua. The expected printing should be 5. However if I put everything in a single file then it's exactly what I want:
In aa.lua:
local x = 5
local f = function()
print(x)
end
f()
Run luajit aa.lua it prints 5.
So why x cannot be seen in the first case?
As their name suggests, local variables are local to the chunk.
dofile() loads the chunk from another file. Since it's another chunk, it makes sense that the local variable x in the first chunk isn't seen by it.
I agree that it is somewhat unintuitive that this doesn't work.
You'd like to say, at any point in the code there is a clear set of variables that are 'visible' -- some may be local, some may be global, but there is some map that the interpreter can use to resolve names of either kind.
When you load a chunk using dofile, then it can see whatever global variables currently exist, but apparently it can't see any local variables. We know that 'dofile' is not like C/C++ inclusion macros, which would give exactly the behavior you describe for local variables, but still you might reasonably expect that this part of it would work the same.
Ultimately there's no answer but "that's just not how they specified the language". The only satisfying answer is probably along the lines 'because otherwise it would cause non-obvious problem X' or 'because then use-case Y would go slower'.
I think the best answer is that, if all names were dynamically rebound according to the scope in which they are loaded when you use loadfile / dofile, that would inhibit a lot of optimization and such when compiling chunks into bytecode. In the lua system, name resolution works like 'either it is local in this scope, and then it binds to that (known) object, or, it is a lookup in the (unique) global table.' This system is pretty simple, there are only a few options and not a lot of room for complexity.
I don't think that running byte code even keeps track of the names of local variables, it discards them after the chunk is compiled. They would have to undo that optimization if they wanted to allow dynamic name resolution at chunk loading time like you suggest.
If your question is not really why but how can I make it work anyways, then one way you can do it is, in the host script, put any local variables that you want to be visible in the environment of the script that is called. When you do this you need to split dofile into a few calls. It's slightly different in lua 5.1 vs lua 5.2.
In lua 5.1:
In a.lua:
local shared = { x = 5 }
temp = loadfile('b.lua')
setfenv(temp, shared)
f = temp()
f()
In lua 5.2:
In a.lua:
local shared = { x = 5 }
temp = loadfile('b.lua', 't', shared)
f = temp()
f()
The x variable defined in module a.lua cannot be seen from b.lua because it was declared as local. The scope of a local variable is its own module.
If you want x to be visible from b.lua, just need to declare it global. A variable is either local or global. To declare a variable as global, just simply do not declare it as local.
a.lua
x = 5
f = dofile'b.lua'
f()
b.lua
local fun = function()
print(x)
end
return fun
This will work.
Global variables live within the global namespace, which can be accessed at any given time via the _G table. When Lua cannot solve a variable, because it's not defined inside the module where is being used, Lua searches that variable in the global namespace. In conclusion, it's also possible to write b.lua as:
local fun = function()
print(_G["x"])
end
return fun

Hello metatable.__len world

A beginner's question about Lua and metatables, with a example as simple as an Hello‑World, involving the len event, which unfortunately does not returns the expected result (I'm using Lua 5.1 installed from Ubuntu's official repository).
The case
Here is the example:
Test_Type = {};
function Test_Type.__len (o)
return 1;
end;
function new_test ()
local result = {};
setmetatable(result, Test_Type);
return result;
end;
do
local a_test = new_test();
print (#a_test);
print(getmetatable(a_test).__len(a_test));
end;
And the result I get:
0
1
I was expecting the first print statement to display 1, but it displays 0, to my big surprise.
What did I missed?
According to Lua Reference Manual — Metatables and Metamethods, the # is equivalent to this:
function len_event (op)
if type(op) == "string" then
return strlen(op) -- primitive string length
else
local h = metatable(op).__len
if h then
return (h(op)) -- call handler with the operand
elseif type(op) == "table" then
return #op -- primitive table length
else -- no handler available: error
error(···)
end
end
end
So print (#a_test); and print(getmetatable(a_test).__len(a_test)); should result into the same, isn't it?
By the way, why is the above excerpt from the Reference Manual refers to metatable(op) while it should be getmetatable(op)? At least I've tried print(metatable(a_test).__len(a_test));, and it ends into an error.
Answer
As Nneonneo noticed, this is an issue with the Lua version in use. Lua 5.2 seems to be required for the above to work.
From http://lua-users.org/wiki/LuaFaq:
Why doesn't the __gc and __len metamethods work on tables?
__len on tables is scheduled to be supported in 5.2. See LuaFiveTwo.
Since you're using 5.1, __len on tables does not work. Indeed, running your code on Lua 5.2 produces
1
1
as expected.

Is the % for upvalue in Lua 5.x deleted

I'm learning Lua from a book, which is a bit old.
I've tried searching the web, but because of the use of the # sign in my search I get really confusing results.
It says that in order to use upvalue you need to use the % sign.
But when I write it in my code I get an error.
Account.new = function (starting_balance)
local self = {}
local balance = starting_balance
self.withdraw = function (v)
%balance = %balance - v;
end
return self
end
error is : unexpected symbol near '%'
Is there a new way to handle upvalues in Lua 5.x ?
Since Lua 5.0, there is no more such thing as a "upvalue sign". An upvalue is a local to the environment a function is declared in, and as such can simply be accessed as any other local variable.
In your case: just use balance instead of %balance.
From Lua 5.1, % is used as modulo operator.
A good but slightly outdated book is the online available version of Programming in Lua, and of course, the reference manual.
Must be a very old book!
The % as upvalue notation was removed in Lua 5.0. (Released 2003)
Since 5.0, Lua has lexical scoping;
that is, upvalues are automatic:
do
local balance = 0
function deposit ( v )
balance = balance + v
return balance
end
end
print ( deposit ( 5 ) )
Output:
5
I'd go with the closure http://www.lua.org/pil/6.1.html#closures

Resources