Managing large indirect userdata

classic Classic list List threaded Threaded
17 messages Options
Reply | Threaded
Open this post in threaded view
|

Managing large indirect userdata

Mark Hamburg-4
Is there any standard advice on how to manage large, indirect (i.e., the
userdata itself stores a pointer to a separate block) userdata? I sometimes
find that my application is choking on large, cached pixmaps and textures. I
need to have something happen to drive the garbage collector forward or
prompt collection in response to the existence of these objects.

Mark


Reply | Threaded
Open this post in threaded view
|

Re: Managing large indirect userdata

Wim Couwenberg-4
> I sometimes
> find that my application is choking on large, cached
> pixmaps and textures. I
> need to have something happen to drive the garbage
> collector forward ...

I ran into this in the exact same way (handling
pixmaps).  A quick 'hack' could be to give
lua_newuserdata an additional size_t parameter that
indicates the extra amount of space related to this
udata (can be 0).  The implementation is pretty
simple: luaS_newudata (in lstring.c) also gets this
extra parameter and simply stores the sum of actual
and extra size in the 'len' field of the new udata. 
Changes are minimal (lua_boxpointer macro should
change as well).

The 'extra' size will be fixed after creation though. 
This may or may not be a problem?

Makes some sense?

--
Wim



		
__________________________________
Do you Yahoo!?
New and Improved Yahoo! Mail - Send 10MB messages!
http://promotions.yahoo.com/new_mail 

Reply | Threaded
Open this post in threaded view
|

Re: Managing large indirect userdata

Jamie Webb-3
In reply to this post by Mark Hamburg-4
On Sun, Sep 12, 2004 at 11:04:43AM -0700, Mark Hamburg wrote:
> Is there any standard advice on how to manage large, indirect (i.e., the
> userdata itself stores a pointer to a separate block) userdata? I sometimes
> find that my application is choking on large, cached pixmaps and textures. I
> need to have something happen to drive the garbage collector forward or
> prompt collection in response to the existence of these objects.

Can't you just do it the same way Lua does? I.e. keep your own
independent byte count and threshold for this data and force a Lua GC
when an allocation of one of these objects would cause the former to
exceed the latter. That should work as long as your code continues to
turn over objects; if there are points where you suddenly have a lot
of references released, and no new allocations are happening, I
imagine you could figure out when that is and add extra manual GC
cycles.

-- Jamie Webb

Reply | Threaded
Open this post in threaded view
|

Re: Managing large indirect userdata

Mark Hamburg-4
That's sort of what I'm doing right now. I'm not sure what the best approach
is for Lua 5.1.

Mark

on 9/12/04 4:34 PM, Jamie Webb at [hidden email] wrote:

> On Sun, Sep 12, 2004 at 11:04:43AM -0700, Mark Hamburg wrote:
>> Is there any standard advice on how to manage large, indirect (i.e., the
>> userdata itself stores a pointer to a separate block) userdata? I sometimes
>> find that my application is choking on large, cached pixmaps and textures. I
>> need to have something happen to drive the garbage collector forward or
>> prompt collection in response to the existence of these objects.
> 
> Can't you just do it the same way Lua does? I.e. keep your own
> independent byte count and threshold for this data and force a Lua GC
> when an allocation of one of these objects would cause the former to
> exceed the latter. That should work as long as your code continues to
> turn over objects; if there are points where you suddenly have a lot
> of references released, and no new allocations are happening, I
> imagine you could figure out when that is and add extra manual GC
> cycles.
> 
> -- Jamie Webb


Reply | Threaded
Open this post in threaded view
|

Child states and garbage collection

Matthew Harmon
Hi all:

I experienced something strange while running scripts in "child" states from
a master state.  Essentially, I was doing this to load a script and save
it's entry point:
                                                                         
    luaL_loadfile(childState, fileName);
    entryPointRef = luaL_ref(childState, LUA_REGISTRYINDEX);

However, this seems to have created a reference to childState such that it
was never completely garbage collected.  I could see lua_getgccount()
constantly growing as I reloaded/ran the script.  Then, I learned to do
this:

    luaL_unref(childState, LUA_REGISTRYINDEX, entryPointRef);

Viola!  The memory usage stops growing and GC seems to always work back to a
"steady state".  Made sense to me.  The script can now be GC'd because
nobody holds a reference to it.

EXCEPT... on occasion, the script seems to be GC'd before I do the unref()
and it crashes when I get to the unref().  Can anybody give me a hint about
this.  Am I approaching this totally wrong?

I managed to solve the problem by creating my references in the "master"
state, but I'd still like to understand what is going on under the hood.

Many thanks,

Matt




Reply | Threaded
Open this post in threaded view
|

Re: Managing large indirect userdata

Edgar Toernig
In reply to this post by Mark Hamburg-4
Mark Hamburg wrote:
>
> Is there any standard advice on how to manage large, indirect (i.e., the
> userdata itself stores a pointer to a separate block) userdata? I sometimes
> find that my application is choking on large, cached pixmaps and textures. I
> need to have something happen to drive the garbage collector forward or
> prompt collection in response to the existence of these objects.

How about this? lua_gcaccount:

  http://lua-users.org/lists/lua-l/2000-12/msg00112.html

Ciao, ET.

Reply | Threaded
Open this post in threaded view
|

Re: Child states and garbage collection

Jamie Webb-3
In reply to this post by Matthew Harmon
On Sun, Sep 12, 2004 at 07:43:35PM -0500, Matthew Harmon wrote:
> EXCEPT... on occasion, the script seems to be GC'd before I do the unref()
> and it crashes when I get to the unref().  Can anybody give me a hint about
> this.  Am I approaching this totally wrong?

Calling luaL_unref() with a bad reference should not cause a crash. I
suspect that what has actually happened is that your child state
itself has been GCed. You need to make sure a reference to the state
exists, e.g. in the registry, until you have finished with it.

> I managed to solve the problem by creating my references in the "master"
> state, but I'd still like to understand what is going on under the hood.

References are stored in the registry, which is shared between a
'master' and all its children. So, this change is probably just
working around the actual problem by avoiding referring to the
deceased child.

-- Jamie Webb

Reply | Threaded
Open this post in threaded view
|

RE: Child states and garbage collection

Matthew Harmon
Thanks... I did have a handle on that, I should have been more clear.  The
child state definitely got GCed as you said.

I guess what I'm confused about is this... should doing my
luaL_ref(childState, LUA_REGISTRYINDEX) with the entry point be enough to
prevent GCing of the childState?  Since the reference is "within itself",
does that count?  It seems so because my memory usage keeps creeping up if I
don't luaL_unref().  Yet... sometimes one get's GC'd and causes that crash.


I guess I'm just trying to reconcile those two seemingly contradictory
behaviors.

-----Original Message-----
From: [hidden email]
[[hidden email]] On Behalf Of Jamie Webb
Sent: Sunday, September 12, 2004 8:56 PM
To: Lua list
Subject: Re: Child states and garbage collection

On Sun, Sep 12, 2004 at 07:43:35PM -0500, Matthew Harmon wrote:
> EXCEPT... on occasion, the script seems to be GC'd before I do the unref()
> and it crashes when I get to the unref().  Can anybody give me a hint
about
> this.  Am I approaching this totally wrong?

Calling luaL_unref() with a bad reference should not cause a crash. I
suspect that what has actually happened is that your child state
itself has been GCed. You need to make sure a reference to the state
exists, e.g. in the registry, until you have finished with it.

> I managed to solve the problem by creating my references in the "master"
> state, but I'd still like to understand what is going on under the hood.

References are stored in the registry, which is shared between a
'master' and all its children. So, this change is probably just
working around the actual problem by avoiding referring to the
deceased child.

-- Jamie Webb





Reply | Threaded
Open this post in threaded view
|

Re: Child states and garbage collection

Roberto Ierusalimschy
> should doing my luaL_ref(childState, LUA_REGISTRYINDEX) with the entry
> point be enough to prevent GCing of the childState?

By default, child threads share the registry with the main thread.
If you do not change that, then your references are kept at the main
registry, and therefore should not be collected. However, if you change
a child's registry, then any reference to the child in its own registry
is a circular reference, and as such is not enough to prevent GC.

As a matter of style, you should keep the child's reference in the main
registry, even if it happens to be the same registry as the child's.

(Just in case: what version of Lua are you using?)

-- Roberto

Reply | Threaded
Open this post in threaded view
|

Re: Child states and garbage collection

Jamie Webb-3
In reply to this post by Matthew Harmon
On Sun, Sep 12, 2004 at 10:37:43PM -0500, Matthew Harmon wrote:
> Thanks... I did have a handle on that, I should have been more clear.  The
> child state definitely got GCed as you said.
>
> I guess what I'm confused about is this... should doing my
> luaL_ref(childState, LUA_REGISTRYINDEX) with the entry point be enough to
> prevent GCing of the childState?  Since the reference is "within itself",
> does that count?  It seems so because my memory usage keeps creeping up if I
> don't luaL_unref().  Yet... sometimes one get's GC'd and causes that crash.

You're confusing the two separate things which you need to keep track
of:

- The child state itself. This is the object that is pushed onto the
  stack as a result of calling lua_newthread. Even though you have a
  C-side pointer to this object (a lua_State), it is still subject to
  being GCed if you don't save the 'Lua half' of it somewhere, at
  which point the C-side pointer will be left dangling, hence the
  crash. Doing a luaL_ref on that object will indeed preserve it, but
  it doesn't sound like you're doing that. So, this is being GCed. You
  need to add refs and unrefs for this object.

- The script which will be run in the child state. You're saving a
  reference to this, and it won't be GCed unless you unref it again
  /even if the state it was going to run in has been GCed/. Hence the
  creeping memory usage you saw. You're treating this object
  correctly, but seeing problems when you try to unref it because of
  the above.

-- Jamie Webb

Reply | Threaded
Open this post in threaded view
|

RE: Child states and garbage collection

Matthew Harmon
In reply to this post by Roberto Ierusalimschy
Many thanks for the info.  I'm using 5.0.2

I don't change the setup, so I guess my reference has always been in the
main thread.  Now, instead of accessing the registry via the childState, I
access it via the masterState and I avoid the crash.  (And I am very happy!)

So, the only remaining question is why a childState seems to have
occasionally been GC'd even when I saved a reference to it.  I was doing
this "via" the childState, but since it should have been using the
masterState's registry anyway, I would have expected the thread to live on
until I removed the reference.  (Of course, I may have some other bug here!)

Is there any other way a thread can be GC'd.  Any special considerations for
it's yield state?

>By default, child threads share the registry with the main thread.
If you do not change that, then your references are kept at the main
registry, and therefore should not be collected. However, if you change
a child's registry, then any reference to the child in its own registry
is a circular reference, and as such is not enough to prevent GC.<






Reply | Threaded
Open this post in threaded view
|

Proper Stack Cleaning

Matthew Harmon
In reply to this post by Roberto Ierusalimschy
I recently activated the runtime checks/tests and found that one of my bugs
was likely due to a stack overflow.  I soon realized that there were some
operations I was performing that left values on the stack.  For example,
loading libraries with lua_xxxlibopen(), etc. may push values onto the
stack, according to lua_gettop() anyway.  In fact, by the time I got to do
any "real" work, the stack had 13 items on it.

In the examples and docs, I don't see mention of cleaning up the stack after
operations like these.  Is it good practice to simply do a lua_settop(l, 0)
after a bunch of such operations?

Also, what is the proper procedure for enabling the runtime checks?  I
manually included luauser_tests.h and added ltests.c to my project.  That
seemed to work, but I'm not sure if that is the recommend method.




Reply | Threaded
Open this post in threaded view
|

Re: Child states and garbage collection

Roberto Ierusalimschy
In reply to this post by Matthew Harmon
> I don't change the setup, [...]

Forget my concerns. Jamie Webb went right to the problem.


> Also, what is the proper procedure for enabling the runtime checks?  I
> manually included luauser_tests.h and added ltests.c to my project.
> That seemed to work, but I'm not sure if that is the recommend method.

You can define LUA_USER_H='"ltests.h"' to include ltests.h. But there is
no "recommend method" as such. Some of those tests are too hard for most
uses (e.g., the define of LUAL_BUFFERSIZE as 27 to force weird buffer
allignment situations). We are changing luaconf to make it easier to
turn on all asserts without using other tests.

-- Roberto

Reply | Threaded
Open this post in threaded view
|

RE: Proper Stack Cleaning

Incley, Mark
In reply to this post by Matthew Harmon
Hi,

I wrote the following macros which I use in most functions that call Lua.
They've proved very useful and have caught quite a few stack "leaks" as well
as drawing my attention to the way that the lua_xxlibopen() functions leave
a value on the stack.


#ifdef _DEBUG
#define	STACKCHECKSTART(ls)		int __stackTopEnd_##ls = 0; int
__stackTopStart_##ls = lua_gettop(ls);
#define	STACKCHECKEND(ls)		STACKCHECKENDPLUS(ls, 0)
#define STACKCHECKENDPLUS(ls,a)	assert((__stackTopEnd_##ls = lua_gettop(ls))
== __stackTopStart_##ls + a);
#else
#define	STACKCHECKSTART(ls)
#define STACKCHECKEND(ls)
#define STACKCHECKENDPLUS(ls,a)
#endif

Example

void egfunction(lua_State *ls)
{
	STACKCHECKSTART(ls)

	<use the Lua API>

	STACKCHECKEND(ls)
		- or -
	STACKCHECKENDPLUS(ls, 2)	/* Should leave fn with 2 extra
values on Lua stack */
	return;
}

Mark.

-----Original Message-----
From: [hidden email]
[[hidden email]] On Behalf Of Matthew Harmon
Sent: 13 September 2004 14:29
To: 'Lua list'
Subject: Proper Stack Cleaning



I recently activated the runtime checks/tests and found that one of my bugs
was likely due to a stack overflow.  I soon realized that there were some
operations I was performing that left values on the stack.  For example,
loading libraries with lua_xxxlibopen(), etc. may push values onto the
stack, according to lua_gettop() anyway.  In fact, by the time I got to do
any "real" work, the stack had 13 items on it.

In the examples and docs, I don't see mention of cleaning up the stack after
operations like these.  Is it good practice to simply do a lua_settop(l, 0)
after a bunch of such operations?

Also, what is the proper procedure for enabling the runtime checks?  I
manually included luauser_tests.h and added ltests.c to my project.  That
seemed to work, but I'm not sure if that is the recommend method.


Reply | Threaded
Open this post in threaded view
|

RE: Proper Stack Cleaning

Matthew Harmon
Great, thanks so much.  I got so worried about this that I have a debug
display in my game that lists every state and it's current stack situation!

>>I wrote the following macros which I use in most functions that call Lua.
They've proved very useful and have caught quite a few stack "leaks" as well
as drawing my attention to the way that the lua_xxlibopen() functions leave
a value on the stack.<< 




Reply | Threaded
Open this post in threaded view
|

RE: Child states and garbage collection

Matthew Harmon
In reply to this post by Jamie Webb-3
Ahhh... thank you very much.

So, is it true that having a SCRIPT loaded (and a reference to it saved), is
not enough to prevent the STATE in which it is loaded from being GC'd?

I guess this was my incorrect assumption.


>>You're confusing the two separate things which you need to keep track
of:

- The child state itself. This is the object that is pushed onto the
  stack as a result of calling lua_newthread. Even though you have a
  C-side pointer to this object (a lua_State), it is still subject to
  being GCed if you don't save the 'Lua half' of it somewhere, at
  which point the C-side pointer will be left dangling, hence the
  crash. Doing a luaL_ref on that object will indeed preserve it, but
  it doesn't sound like you're doing that. So, this is being GCed. You
  need to add refs and unrefs for this object.

- The script which will be run in the child state. You're saving a
  reference to this, and it won't be GCed unless you unref it again
  /even if the state it was going to run in has been GCed/. Hence the
  creeping memory usage you saw. You're treating this object
  correctly, but seeing problems when you try to unref it because of
  the above.
<<





Reply | Threaded
Open this post in threaded view
|

Re: Child states and garbage collection

Jamie Webb-3
On Mon, Sep 13, 2004 at 09:14:50AM -0500, Matthew Harmon wrote:
> Ahhh... thank you very much.
> 
> So, is it true that having a SCRIPT loaded (and a reference to it saved), is
> not enough to prevent the STATE in which it is loaded from being GC'd?

Correct. The idea of a script being loaded within a given state is not
even valid. The loaded script is (potentially) available to all the
connected states. You just choose to execute it within a particular
state (and so for convenience load it such that it is placed on the
stack of that state).

-- Jamie Webb