lua-users home
lua-l archive

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


Hello,

I'm having a bit of an issue while interfacing Lua to an existing C++
framework, in particular with the garbage collector. I'd be very
grateful if someone could give me some good advice:

I'm working at a company that is developing a dataflow programming
system, and I'm trying to embed Lua in it, to deal with those parts of
the problem that really doesn't fit that well in a dataflow paradigm,
and to be able to prototype things. I'm in the machine vision field, so
the data types that are floating around in the system range from simple
integers via lookup-tables to massive multi-megabyte images. Our
existing (C++ based) framework uses a container class that is able to
contain any kind of data, and is based around a shared_pointer to a
boost::any holding a pointer to the target data. 

Without going into detail too much, the basic idea is:

class CBlockDataObject
{
   public:
	boost::shared_ptr<boost::any> mContent;
}

There's a some additional accounting fields, and a lot of template
memberfunctions to facilitate access to the contained data. I went ahead
and wrote a Lua binding for this kind of object, and it seems to be
working 
reasonably well. I implemented it as a userdatum with metatable; the
entire code is too large to post here right away, and contains a lot of
irrelevant stuff, but this is how my constructor looks like:

// Exported function: Create a CBlockDataObject on the top of stack L
CBlockDataObject *ELuaBindings::PushCBlockDataObject(lua_State *L) {
  void *buffer = lua_newuserdata(L, sizeof(CBlockDataObject));
  CBlockDataObject *newObject = new (buffer) CBlockDataObject;
  // Set the metatable for this object
  luaL_getmetatable(L, METATABLENAME);
  lua_setmetatable(L, -2);

  return newObject;
}

At this point, the generated container is empty, it doesn't contain
anything except for an "I am empty" placeholder. I have other member
functions to create an object inside my container, and to access it.

The metatable contains a __gc metamethod, which points to the following
function:

int CBlockDataWrapper::Destroy(lua_State *L)
{
  CBlockDataObject *object = static_cast<CBlockDataObject
*>(luaL_checkudata(L, 1, METATABLENAME));
  luaL_argcheck(L, object != NULL, 1, "'BlockDataObject' expected");
  object->~CBlockDataObject();
  return 0;
}

My problem is that this metamethod isn't called often enough, because
Lua only knows about the memory usage of the container class itself
(which is somewhere around 32 bytes if memory serves me right, so hardly
interesting) and has no way of knowing that this content is basicly a
refcounted pointer to something that might be a lot bigger - possibly
several megabytes, meaning that the *true* memory footprint might be a
multitude more in case this particular CBlockDataObject was the last one
pointing at the contents.

As far as I can see, I have the following alternatives:

1) Manually call the garbage collector on a very regular basis. This
solves the memory problem, but it strikes me
   as being not very efficient.
2) have *all* memory allocation be done by Lua. This won't fly, because
that's not how the system works. The whole system is based around the
possibility to have multiple references to a single object, and the only
way to implement it with full in-place storage would be to make private
copies of all incoming data. This would imply copying all data,
including that multi-megabyte image, and I'd have to go the opposite
route when data leaves my Lua-block. That'd be going from bad to worse;
calling the garbage collector some more is much much better.
Furthermore, since the contents of my container is opaque, there is no
safe way to make a copy. 
3) Tweak the garbage collector, so that it bases its decision on the
size of the payload instead of the size of the container. There's one
catch: I don't know what the payload is. It can be anything, and there's
no universal way to retrieve the true size. I could of course implement
this for a few common datatypes that will most likely be huge, and I
guess it's better than 1), but I'm a bit worried that there will be
lingering issues with this, and it's fragile at best.

However, iff there is a simple and efficient way to determine whether a
userdatum at a given block address still has references to it, I have
another solution that seems to me like it's a good compromise:

4) Keep track of all the CBlockDataObjects generated for Lua (no problem
there; I could easily store them in a linked list), and iterate over all
outstanding containers on a regular basis. If a container has no (Lua)
references to it, release the object it holds, and let Lua's garbage
collector deal with cleaning up the now empty container at its own pace.
This seems like the most efficient way by far; the amount of outstanding
containers is probably going to be relatively small, and the lossage due
to containers that haven't been collected is small as long as they're
empty. Furthermore, lua's garbage collector will Do The Right Thing,
then, since its idea of memory usage will be correct.

However, for this to work I need an efficient way to determine whether a
container is no longer referenced by Lua. Any advice would be more than
welcome.

I'm sorry I rambled away, and thank you for reading all of this,

Martijn.