Question

I use LuaJit for extending a plain C application (using the Lua C API). The host application does manage memory for a lot of objects that I have written wrappers for in Lua.

Now I would like to be able to delete objects from within the lua function, i.e. implement a delete function. I would like to illustrate the problem at hand with the following outline of the problem.

Basically my lua user data structure looks something like this.

struct my_lua_container {
     size_t obj_db_index;
};

where obj_db_index is an index for the local object database. With the Lua C API I have created a lua-function query_object(...) that retrieves a lua metatable based on this user data and offering an API for managing the db object.

I am now planning to introduce a method my_db_object:delete() in the metatable API. :delete() could invalidate my_db_object by overwriting the variable with 0 or setting another member variable. The problem however is, that all references to the deleted object ought to be invalidated. Consider this lua code:

local p = query_object("1")
local q = query_object("1")
p:delete()
q:do_something() -- <=== q still contains a obj_db_index

Now I wonder how to solve this potential conflict. The two main problems are:

  • An invalid obj_db_index could be an invalid index. This is actually probably caught by the code already, so it isn't pretty but alright

  • after deletion, the index might be reused and this could lead to subtle errors when other references still use the old index.

What are strategies to deal with this?

My idea might be a little time-consuming but this would be ok in the event of deletion:

  • Is there some Introspection that I can perform on user data objects? Like iterating over all user data objects with the same type in order to invalidate my_db_index when the deletion is triggered
Was it helpful?

Solution

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

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top