spooky gc behavior
Consider:
local r = @record{n: integer}
function r:__gc() print "gc'd" end
require 'allocators.default'
require 'allocators.gc'
local function main()
local c = new(r)
print(c.n)
gc:collect()
print 'done'
end
main()And running it:
$ nelua gced.nelua 0 done gc'd $ nelua -r gced.nelua 0 gc'd done
This is what's happening: in a normal build, the 'c' variable isn't garbage-collected until after 'main' returns, because 'c' is on the stack and the GC scans the stack and decides that 'c' is alive.
In a release build, the 'c' variable occupies a register instead of the stack, and is garbage-collected by gc:collect()
Hit in the wild by a D program, and discussed on the D forums.
The workarounds are similar:
to try to ensure an additional GC-traced pointer to the allocation:
local r = @record{n: integer}
function r:__gc() print "gc'd" end
require 'allocators.default'
require 'allocators.gc'
local function main()
local c = new(r)
local p = &c;
gc:register(p, #[r.value.size]#)
defer gc:unregister(p) end
print(c.n)
gc:collect()
print 'done'
end
main()and putting the pointer in static memory:
local r = @record{n: integer}
function r:__gc() print "gc'd" end
require 'allocators.default'
require 'allocators.gc'
local c: *r
local function main()
c = new(r)
print(c.n)
gc:collect()
print 'done'
end
main()The trouble is knowing when to do that. Fortunately this is a very hard condition to encounter, as it requires you to expect stack lifetimes to continue past the last usage of a variable, which is a strange thing to expect (in the linked program, the only other live reference to an object was passed into kernel-space where the GC can't see, and was destroyed before getting passed back from the kernel).