Lua(LuaJit)和C语言中的对象生命周期。

我使用 LuaJit 扩展了一个纯C应用程序(使用Lua C API)。主机应用程序确实为我在Lua中编写的许多对象管理内存。

现在我想要在lua函数中删除对象,即实现删除函数。我想用以下问题大纲来说明问题。

基本上,我的Lua用户数据结构看起来像这样。

struct my_lua_container {
     size_t obj_db_index;
};

其中obj_db_index是本地对象数据库的索引。使用Lua C API,我创建了一个lua函数query_object(...),根据此用户数据检索lua元表,并提供管理db对象的API。

现在我计划在元表API中引入一个方法my_db_object:delete():delete()可以通过将变量覆盖为0或设置另一个成员变量来使my_db_object无效。但是问题在于,应该使所有对已删除对象的引用无效。考虑这个lua代码:

local p = query_object(“ 1”)
local q = query_object(“ 1”)
p:delete()
q:do_something() -  <===q仍然包含一个obj_db_index

现在我想知道如何解决这个潜在的冲突。两个主要问题是:

  • 无效的obj_db_index可能是一个无效的索引。实际上,这可能已经被代码捕获,所以这很漂亮但可以。

  • 删除后,可能会重新使用索引,当其他引用仍使用旧索引时,这可能导致微妙的错误。

如何处理这种情况?

我的想法可能会有点费时,但在删除时这是可以的:

  • 是否可以对用户数据对象执行某些内省?例如,遍历所有具有相同类型的用户数据对象,以便在触发删除时使my_db_index无效
点赞
用户3125367
用户3125367

也许有点晚了,但是解决方法是将新对象放入弱表中,永远不要创建已经存储在其中的对象。

-- 这应该是真正的C语言,但为了可读性,我们用Lua伪代码来编写
registry.my_cache = setmetatable({ }, { __mode = "v" })

function push_object(db_id)
    local object = registry.my_cache[db_id]
    if object == nil then
        object = lua_newuserdata(db_id)
        registry.my_cache[db_id] = object
    end
end

assert(push_object(1) == push_object(1))

现在,只有唯一的db_id从C侧到Lua侧,问题几乎消失了。

但是还有一个细节需要注意。userdata的垃圾收集有两个阶段:终结和从弱表中删除。有时,在userdata被终结但仍存在于弱表中的时刻,上面的代码可能会将已终结的userdata返回给用户。需要进行额外的检查,如果ud已经被终结,应该先手动将其从表中删除。

function push_object(db_id)
    local object = registry.my_cache[db_id]

    -- check vitality first
    if is_finalized(object) then
        registry.my_cache[db_id] = nil
        object = nil
    end

    if object == nil then
        object = lua_newuserdata(db_id)
        registry.my_cache[db_id] = object
    end
end

如何知道userdata是否已终结取决于终结方法(metatable.__gc)的实现方式。

2014-01-16 05:31:45