lua-users home
lua-l archive

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


Hello,

I have problem with userdata and garbage collection, until now I thought that after userdata __gc metamethod is called it means that lua cannot touch my data in any way, so I can free my resources. However, it is not the case, there is possibility that table's __gc metamethod can touch
my userdata after it was collected.

Because my English is far from perfect I attach example to show what I mean.

How can I be sure that my userdata is gone, and I can free its resources?
Only solution that I see is to mark userdata as free on __gc and then check for that it in every function exposed to lua. Is there a better way? Or is it possible to disable __gc on tables?
My lua version is 5.2.2.

Regards
Dominik
#include <assert.h>
#include <stdio.h>
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>

// "free" resources
static int ud_gc(lua_State *L)
{
	int *ud = luaL_checkudata(L, 1, "ud");
	*ud = 0;
	puts("ud gc");
}

// print userdata resources
static int ud_print(lua_State *L)
{
	int *ud = luaL_checkudata(L, 1, "ud");
	assert(*ud > 0);
	printf("%d\n", *ud);
}

int main(void)
{
	lua_State *L = luaL_newstate();
	luaL_openlibs(L);			// oddly, without this lua doesn't call
								// table's __gc metamethods

	// table with __gc method, which prints userdata
	const char *code = 
"tab = {} \
setmetatable(tab, {__gc = function (self) ud_print(self[1]) end })";

	luaL_dostring(L, code);

	// new userdata
	int *ud = lua_newuserdata(L, sizeof (*ud));
	*ud = 1;

	// metatable for userdata
	luaL_newmetatable(L, "ud");
	lua_pushcfunction(L, ud_gc);
	lua_setfield(L, -2, "__gc");
	lua_setmetatable(L, -2);

	// set new userdatad as global var
	lua_setglobal(L, "ud");

	// set test func as global var
	lua_pushcfunction(L, ud_print);
	lua_setglobal(L, "ud_print");

	luaL_dostring(L, "tab[1] = ud");
	luaL_dostring(L, "ud = nil");
	luaL_dostring(L, "tab = nil");

	// at this point ud is no longer reachable, so it's __gc method
	// is called, but its still accessed in tab's __gc method
	lua_gc(L, LUA_GCCOLLECT, 0); // assertion failed

	return 0;
}