Add cleanup for indexes() and values()

More testing required.
This commit is contained in:
Aaron Smith 2023-08-17 14:44:45 +01:00
parent bf1271de23
commit 463faee85a
4 changed files with 224 additions and 44 deletions

View file

@ -3,34 +3,48 @@ if not game then script = require "test/relative-string" end
local ref_to_id = {} :: { [string]: number }
local id_to_ref = {} :: { [number]: string }
local cleanup_callbacks = {} :: { [number]: () -> () } -- always dense
local cleanup_callers = {} :: { [number]: () -> () } -- can be sparse
setmetatable(cleanup_callers :: any, { __mode = "v" })
local cleanup_lifetime = {} :: { [number]: unknown } -- can be sparse
setmetatable(cleanup_lifetime :: any, { __mode = "v" })
local manual_mode = {
caller = false :: false | () -> (),
callbacks = {} :: { () -> () }
}
-- todo: rare case where mem address is reused by another function on same line
-- does this case handle itself?
local function cleanup(callback: () -> ())
local caller = debug.info(2, "f") :: () -> ()
local line = debug.info(2, "l") :: number
local ref = tostring(caller) .. line
local function cleanup_ref(ref: string, lifetime: unknown, callback: () -> ())
local id = ref_to_id[ref]
if id then
cleanup_callbacks[id]()
cleanup_lifetime[id] = lifetime -- rare case where ref is reused while lifetime is nil
else
id = #cleanup_callbacks + 1
ref_to_id[ref] = id
id_to_ref[id :: any] = ref -- todo
cleanup_callers[id :: any] = caller -- todo
cleanup_lifetime[id :: any] = lifetime -- todo
end
cleanup_callbacks[id] = callback
end
local function cleanup(callback: () -> ())
local lifetime = debug.info(2, "f") -- `caller of cleanup() is lifetime of cleanup`
local line = debug.info(2, "l")
if manual_mode.caller == lifetime then
table.insert(manual_mode.callbacks, callback)
else
local ref = tostring(lifetime) .. line
cleanup_ref(ref, lifetime, callback)
end
end
local function clean_garbage()
for id = #cleanup_callbacks, 1, -1 do
if cleanup_callers[id] == nil then -- caller was garbage collected
if cleanup_lifetime[id] == nil then -- lifetime was garbage collected
local callback = cleanup_callbacks[id]
do -- swap and pop
@ -39,8 +53,8 @@ local function clean_garbage()
cleanup_callbacks[id] = cleanup_callbacks[max_id]
cleanup_callbacks[max_id] = nil
cleanup_callers[id] = cleanup_callers[max_id]
cleanup_callers[max_id] = nil
cleanup_lifetime[id] = cleanup_lifetime[max_id]
cleanup_lifetime[max_id] = nil
local ref = id_to_ref[id]
local max_ref = id_to_ref[max_id]
@ -58,4 +72,16 @@ local function clean_garbage()
end
end
return function() return cleanup, clean_garbage end
local manual_cleanup_mode = function(caller: () -> ()?)
if caller == nil then
local clone = table.clone(manual_mode.callbacks)
manual_mode.caller = false
table.clear(manual_mode.callbacks)
return clone
else
manual_mode.caller = caller
end
return manual_mode.callbacks
end :: ( (caller: (...any) -> ()) -> () ) & ( (nil) -> { () -> () } )
return function() return cleanup, clean_garbage, manual_cleanup_mode, cleanup_ref end