This cart shows what I think is a bug with the garbage collector interacting with weakrefs. Nothing should be drawn, yet sprite zero is shown. I'm not entirely sure how to show code here? I'm new!
Here are the steps:
Create a table with weakref values.
Add a value to that table.
Pass that table to a function.
The function uses a LOCAL variable to iterate over ALL() values of the table.
Return
The LOCAL variable holds a reference to the last member of the table. The LOCAL variable doesn't appear to get correctly garbage collected, and so neither does the last table member.
Weirdly, if I run stat(0) inside the function with the iterator, all is well. If I run it outside the function, nothing works.
I tried it, and putting stat(0) anywhere where it'd be executed every frame causes the garbage collection to run and the sprite to disappear.
So without stat(0), the sprite will only disappear if something else causes the garbage collector to run. It's not a bug if it doesn't run - that just means that it has no reason to run and you cannot assume that weak values will be collected then.
Normally, the garbage collector absolutely needs to run because every call to all() allocates new upvalues (that end up on the heap because all() exits) and a new closure.
However, lua has some pretty complex logic to cache both closures and (I think) upvalues, and I'm guessing that logic ends up preventing garbage from building up, though I'm not sure of the specifics.
It's particularly weird that the "local i" is needed for the garbage collector to not need to run. Though note that:
- The "local i" declares an unused variable, since "for i" creates its own distinct local variable (just with the same name). so "local i" can be changed to "local w" with the same effect.
- The garbage collection is only avoided when exactly one extra local is in group_drw - 0 or 2 or more don't seem to work.
- If you remove group_drw and inline it into draw - you can get the same effect by having 3 extra locals instead of 1. So I'm guessing the effect occurs when the locals in the all() call (or something else?) end up in a specific location in the stack.
Also, this is kinda tricky to debug because both print and printh allocate memory, causing the effect to not occur...
This is a highly reduced test case, in the full game placing stat(0) in many places where it runs every frame does NOT resolve the issue.
The reason that the drawing is in a function instead of inline in the draw function is because it also makes the problem go away...
[Please log in to post a comment]