lua-users home
lua-l archive

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


On 29 Aug 2013, at 15:57, Dominik Zaczkowski wrote:

> Thank you Kevin, now i understand why my data is accessed. But I still don't get how __index and __newindex metamethods could help, my functions exposed to lua are not allways userdata methods - and that was your point, to hide them, right?

Yes, my point was to make the methods non-accessible. I understand that not all your functions are userdata methods, however, surely it is only the userdata methods that will be affected by garbage collection. The calling of a __gc method should not affect any global methods or methods of other objects.

> I tried to throw lua_error() in my wrapper function which gets the userdata, but sadly i got panic on lua_gc(),

That is because an error is being thrown and lua_gc isn't protected. This can be handled by calling lua_gc inside a pcall, you will get a return value of LUA_ERRGCMM (At least you will in 5.2), along with a suitable message on the stack.

Attached is some code that creates two user datas that reference each other and call each other in their gc metamethod. As you can see from the output, the first succeeds, but the second fails with the message "Tried to access a dead userdata", this is done by changing the metatable of the userdata.

Thanks,
Kev

#include <lua5.2/lua.h>
#include <lua5.2/lauxlib.h>
#include <stdio.h>
#include <assert.h>

static int accessInvalidObj(lua_State *l) {
	return luaL_error(l, "Tried to access a dead user data");
}

static int setDeadObjMT(lua_State *l) {
	lua_settop(l, 1);
	
	lua_rawgetp(l, LUA_REGISTRYINDEX, setDeadObjMT);
	if(lua_type(l, -1) == LUA_TNIL) {
		lua_newtable(l);
		lua_replace(l, 2);

		lua_pushcfunction(l, accessInvalidObj);
		lua_setfield(l, -2, "__index");
		
		lua_pushcfunction(l, accessInvalidObj);
		lua_setfield(l, -2, "__newindex");

		lua_pushvalue(l, -1);
		lua_rawsetp(l, LUA_REGISTRYINDEX, setDeadObjMT);
	}

	lua_setmetatable(l, 1);
	return 0;
}

static int userdataGC(lua_State *l) {
	const char *code = "local uv = ... for k,v in pairs(uv) do print(k, v:alive()) end";
	luaL_loadstring(l, code);
	lua_getuservalue(l, 1);
	lua_call(l, 1, 0);
	
	lua_pushcfunction(l, setDeadObjMT);
	lua_pushvalue(l, 1);
	lua_call(l, 1, 0);

	printf("Userdata has been GC'ed\n");
	return 0;
}

static int userdataStore(lua_State *l) {
	lua_settop(l, 3);

	lua_getuservalue(l, 1);
	lua_replace(l, 1);

	lua_settable(l, -3);
	return 0;
}

static int userdataAlive(lua_State *l) {
	lua_pushboolean(l, 1);
	return 1;
}

static int setUserDataMT(lua_State *l) {
	lua_settop(l, 1);
	
	lua_rawgetp(l, LUA_REGISTRYINDEX, setUserDataMT);
	if(lua_type(l, -1) == LUA_TNIL) {
		lua_newtable(l);
		lua_replace(l, 2);

		lua_pushvalue(l, -1);
		lua_setfield(l, -2, "__index");

		lua_pushcfunction(l, userdataGC);
		lua_setfield(l, -2, "__gc");

		lua_pushcfunction(l, userdataStore);
		lua_setfield(l, -2, "store");

		lua_pushcfunction(l, userdataAlive);
		lua_setfield(l, -2, "alive");

		lua_pushvalue(l, -1);
		lua_rawsetp(l, LUA_REGISTRYINDEX, setUserDataMT);
	}

	lua_setmetatable(l, 1);
	return 0;
}

static int newUserData(lua_State *l) {
	lua_settop(l, 0);

	void *ud = lua_newuserdata(l, 1);

	lua_newtable(l);
	lua_setuservalue(l, -2);

	lua_pushcfunction(l, setUserDataMT);
	lua_pushvalue(l, 1);
	lua_call(l, 1, 0);

	return 1;
}

static int protectedgc(lua_State *l) {
	lua_gc(l, LUA_GCCOLLECT, 0);
	return 0;
}

int main(int argc, char **argv) {
	lua_State *l = luaL_newstate();
	luaL_openlibs(l);

	lua_pushcfunction(l, newUserData);
	lua_setglobal(l, "nud");

	const char *code =
	 "do; local a, b = nud(), nud(); a:store(\"b\", b); b:store(\"a\", a); end";

	luaL_loadstring(l, code);
	if(lua_pcall(l, 0, 0, 0) != LUA_OK) {
		printf("pcall failed: %s\n", lua_tostring(l, -1));
	}

	lua_pushcfunction(l, protectedgc);
	if(lua_pcall(l, 0, 0, 0) != LUA_OK) {
		printf("pcall failed: %s\n", lua_tostring(l, -1));
	}

	lua_close(l);
	return 0;
}