lua-users home
lua-l archive

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


It was thus said that the Great Javier Guerra Giraldez once stated:
> On Fri, Apr 4, 2014 at 7:46 PM, Coroutines <coroutines@gmail.com> wrote:
> >  Is there a better way (than object -> string marshalling) ?
> 
> don't move objects around.
> 
> if they're big and/or complex, it's better to designate one thread as
> it's "owner", and other ones just tell it what to do with it.

  But that depends upon how it's defined.

  Just to reiterate, we have two different lua states:

	L1 = luaL_newstate();
	L2 = luaL_newstate();

that we want to move values between.  The easiest way is to treat the object
as a lightuserdata, which can be transfered between the two states quite
easily:

	void *p = lua_touserdata(L1,idx);
	lua_pushlightuserdata(L2,p);

but you'll need to track the lifetime of the object.  A userdata object is a
bit more problematic.  First off, while you could create the object directly
in Lua:

	userdata__s *x = lua_newuserdata(L1,sizeof(userdata__s));

there are a ton of issues with referencing it in another lua state. First,
you need to pin the userdata in its origin state to prevent it from being
garbage collected.

	userdata__s *x = luaL_checkudata(L1,idx,MY_TYPE_OF_DATA);
	x->ref = luaL_ref(L1,LUA_REGISTERYINDEX);

Now we come to an issue.  If we push the address we're given as a
lightuserdata into L2, we can't map a metatable to it.  If our object
doesn't have a metatable, then there isn't an issue.  But in most of my
code, my userdata objects do have a metatable.  And for that, we need to
create a userdata object in the other Lua state.  To do that, we have to
either push the pointer as a userdata, but now we have to have two sets of
routines, one that can take a pointer to our object

	int foo(userdata__s *p) { ... }

and another one that takes a pointer to a pointer:

	int pfoo(userdata__s **pp)
	{
	  return foo(*pp);
	}

and two set of metatable routines, one set that expects a pointer to a
userdata, and one set that expects a pointer to a pointer. The other option
is to make the userdata type a pointer to a pointer, and do reference
counting in the object.  You would create it thus:

	userdata__s  *data;
	userdata__s **pdata;

	data = malloc(sizeof(userdata__s));
	data->ref = 1;
	pdata = lua_newuserdata(L1,sizeof(userdata__s *));
	*pdata = data;
	luaL_getmetatable(L1,MY_TYPE_OF_DATA);
	lua_setmetable(L1,-1);

And to "copy" it to another state:

	userdata__s **pdata1 = luaL_checkudata(L1,idx,MY_TYPE_OF_DATA);
	userdata__s **pdata2 = lua_newuserdata(L2,sizeof(userdata__s *));

	*pdata2 = *pdata1;
	(*pdata1)->ref++;
	luaL_getmetatable(L2,MY_TYPE_OF_DATA);
	lua_setmetatable(L2,-1);

The __gc metamethod of the userdata needs to update the reference count and
when it hits zero, then it can free the actual memory.  Then you have only
one set of routines to deal with the object.

  I'm leaving the Lua table copying up to the reader as an exercise.

  -spc (That's because I'm too lazy to outline what is needed)