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 still contains a obj_db_index

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

  • 无效obj_db_index可能是无效索引。这实际上可能已经被代码捕获了,所以它不漂亮但没关系

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

有什么

策略可以解决这个问题?

我的想法可能有点耗时,但在删除的情况下这没关系:

  • 是否可以对用户数据对象执行一些内省?就像循环访问具有相同类型的所有用户数据对象,以便在触发删除时使my_db_index无效

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

-- this should be really C, but for readability, we write it in Lua pseudocode
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 已经完成,但仍然存在于弱表中,因此上面的代码可能会将最终的用户数据返回给用户。应进行额外的检查,如果 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

如何知道用户数据是否已最终确定取决于您对最终确定方法 (metatable.__gc) 的实现。

相关内容

  • 没有找到相关文章