问题描述
所以。这是一个傻瓜。我正在使用Lua注册表(也称为LUA_REGISTRYINDEX
)存储我稍后要从C调用的Lua回调函数。这非常简单,这是我用于存储和检索Lua回调的实用函数:
bool store_function(lua_State *L,int *storage)
{
if(*storage) {
luaL_unref(L,LUA_REGISTRYINDEX,*storage);
}
if(lua_type(L,-1) == LUA_TFUNCTION)
{
*storage = luaL_ref(L,LUA_REGISTRYINDEX);
return true;
}
else if(lua_type(L,-1) == LUA_TNIL)
{
*storage = 0;
return false;
}
else
{
luaL_error(L,"Invalid function");
return false;
}
}
bool get_function(lua_State *L,int storage)
{
if(storage == 0)
{
return false;
}
lua_rawgeti(L,storage);
if(lua_isnil(L,-1))
{
lua_pop(L,1);
return false;
}
return true;
}
({storage
指向我将索引存储到正确的函数指针的全局int)。
现在,这几乎总是很有效。但是,有时,我的回调混杂在一起,并调用了错误的回调,从而导致热闹错误。
由于它是如此断断续续,因此花了几个月的时间才能确定下来。我已经在自己的代码和lua的标准库(实际上是luajit,但lauxlib代码大多是lua 5.1的复制粘贴)中添加了调试打印。
我开始将索引打印到注册表中,在该注册表中,我的回调最终结束,并注意到它们通常位于索引12、13、14及其附近,除了发生此错误外,此时索引为1072693248。十六进制为0x3FF00000,二进制为00111111111100000000000000000000。那不是巧合。
看起来at the source for luaL_ref
,很明显它应该只使用连续的整数(还要重用旧的插槽),而且我怀疑我的注册表中是否包含十亿个对象。
更多跟踪。我将此补丁添加到了luajit:
diff --git a/src/lib_aux.c b/src/lib_aux.c
index 2682a38..71b68dc 100644
--- a/src/lib_aux.c
+++ b/src/lib_aux.c
@@ -276,6 +276,7 @@ LUALIB_API void luaL_buffinit(lua_State *L,luaL_Buffer *B)
LUALIB_API int luaL_ref(lua_State *L,int t)
{
int ref;
+ const char* how = "free element";
t = abs_index(L,t);
if (lua_isnil(L,-1)) {
lua_pop(L,1); /* remove from stack */
@@ -288,9 +289,11 @@ LUALIB_API int luaL_ref(lua_State *L,int t)
lua_rawgeti(L,t,ref); /* remove it from list */
lua_rawseti(L,FREELIST_REF); /* (t[FREELIST_REF] = t[ref]) */
} else { /* no free elements */
+ how = "created ref";
ref = (int)lua_objlen(L,t);
ref++; /* create new reference */
}
+ printf("refSetting in table %d (%s): at ref %d (%s)\n",t == LUA_REGISTRYINDEX ? "registry" : "user",ref,how);
lua_rawseti(L,ref);
return ref;
}
diff --git a/src/lj_api.c b/src/lj_api.c
index d17a575..bb7b9dd 100644
--- a/src/lj_api.c
+++ b/src/lj_api.c
@@ -996,6 +996,13 @@ LUA_API void lua_rawset(lua_State *L,int idx)
LUA_API void lua_rawseti(lua_State *L,int idx,int n)
{
+ if (idx == -10000) {
+ printf("rawseti in registry[%d] = %d / %.2f\n",n,lua_tointeger(L,-1),lua_tonumber(L,-1));
+ if (lua_tointeger(L,-1) == 1072693248) {
+ printf("\n\noh shit!\n\n");
+ lua_assert(lua_tointeger(L,-1) != 1072693248);
+ }
+ }
GCtab *t = tabV(index2adr(L,idx));
TValue *dst,*src;
api_checknelems(L,1);
以及更高版本,即使发生这种情况,也可以使用堆栈跟踪打印。快进一个月,就会触发:
我的解释是,表的自由列表索引(它将下一个可重复使用的索引存储在其自身的索引0中)最终变得无用,然后整个注册表就停止工作了。
是内存堆损坏了吗?我的lua有毛病吗?我C语言中的错误?十岁的lua lauxlib中的错误?我不知道。
什么是0x3FF00000?值为1.0000的两倍的指数。但是为什么在那里呢?它还用于在cJSON(同一过程中的另一个C库)中解码UTF-16。
我的代码:
任何线索和猜测将不胜感激。
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)