4

我使用LuaJit来扩展一个普通的 C 应用程序(使用 Lua C API)。宿主应用程序确实为我在 Lua 中为其编写包装器的许多对象管理内存。

现在我希望能够从 lua 函数中删除对象,即实现一个删除函数。我想用下面的问题大纲来说明手头的问题。

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

struct my_lua_container {
     size_t obj_db_index;
};

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

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

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触发删除时失效
4

1 回答 1

1

Maybe a little late, but… The solution is to put new objects into weak table and never create objects already stored there.

-- 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))

Now only unique db_id's go from C side to Lua side, problem almostly disappeared.

But there is one more detail to take care of. Garbage-collection of userdata has two phases: finalization and removal from weak tables. There are moments, when userdata is finalized, but still present in weak table, so the code above may return finalized userdata to the user. Additional check should be made, and if ud is finalized, it should be manually removed from table first.

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

How you know whether userdata is finalized is up to your implementation of finalization method (metatable.__gc).

于 2014-01-16T05:31:45.987 回答