lua-users home
lua-l archive

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


I´m very puzzled.
Two days ago, I posted a ´SIGSEGV´ condition, and few hours later, the
explanation.
E.Toernig was also kind enough to maintain a private discussion about the
main topic, which in few words is
Can one safely pop a C function call stack, having this call stack one or
more local variables which are the only reference(s) to collectable
object(s), but taking one such pointer(s) and using it right
after ?
E.T. said me that is not possible, because the now unreferenced objects
could be collected, so the
pointer(s) would be pointing to freed memory, causing some form of memory
access error.
In fact, that could be another form of what I experienced: having allocated
a new userdata chunk,
popping the stack before any decent reference builded up, implicitly calling
the GC ( because I
pushed a string literal ), then pushing again the pointer to the userdata
trying this time to bind it to
its metatable, lasted in a freed pointer access, and the obvious system
error.
But the interesting thing remained: when this maneuver was done having only
a few local userdata
unreferenced, all went ok. The error was raised when there were more ´dead
locals´ to collect.
I tried to understand the details of the marking policies, to help me in the
construction of a strategy
to deal with this situation. My provisory approach was to not pop the
calling stack until done with
all required manipulations, and popping the stack  just before the push of
result(s), or better, to push
all possible results, and then lua_remove all previous call stack.
But before being involved, I tried the scenario armed with a debugger and
patience, to get an
interesting result:
One of the first things the GC does, is to mark all stack objects. At this
time, the stack is the full
stack, i.e. from the stack member of lua_State, to ( top - 1 ), thus
englobing any ´private´ function
stack.
Also, this function sets up an extra value ( ´lim´ ), which is at least the
top of the stack, even if it
may be corrected browsing the call contexts array, for every non C function
call, and checking if that context ´own´ stack top is over ( greater
address ) the current limit.
Once done marking all objects in the stack, the functions sets to nil
everything over the stack top
and the limit, this one included. So, at least the stack top is set to nil.
It seems to me that this is a
clean up of the stack.
But the net result is that primarily, all stack objects are marked.
I tested and debugged this time using a local table created in a lua
function as an argument to a C function. This C function in turn retrieves 3
string keyed entries from the table ( so pushing a string, forcing GC
cycles ).
After trying 10000+ iterations of this call sequence, no error happened.
Because the lua function pass only the local table to the C function, no
doubt it´s on the stack top,
and after being popped, the stack top is no longer ´marked´ by the
traversestack function, rather,
it´s set to nil.
To try emulate the userdata example the lua function, aside the argument
table, constructs 3 more
tables, to get the GC plenty of garbage to play with.
Can the lua authors or someone explain me where is the difference between a
userdata popped
stack entry, and a C function popped stack entry, so the first may lead to
an invalid pointer because
of collection, while not so the second ?
Also, why the difference between the userdata example scripts, where the one
that crashes departed
only in the amount of garbage generated per execution cycle ?
At first, it seemed obvious to me, but now all this is becoming a matter of
wizardry.
Thanks in advance