gears.object:weak_connect_signal: Use a weak-value table (#520)
Lua will remove objects as values from a weak table before these objects are finalized, but as values only in the next garbage collection cycle after the object was finalized. Up to now, gears.object uses a table with weak keys so that :disconnect_signal() works. This means that a signal can still call methods which were already considered garbage by the garbage collector and thus can use userdata from the C side which was already finalized. Crashes and other bugs result. This commit changes the code so that the function is also a value in the weak table. Thus, the GC will remove the entry before the object is finalized. Special magic is needed for Lua 5.1, because there only userdata has the behavior that we want while we have a function. We do some magic with function environments to make this work... Closes https://github.com/awesomeWM/awesome/pull/567. Signed-off-by: Uli Schlachter <psychon@znc.in>
This commit is contained in:
parent
0fdecfb2df
commit
ce965424c3
|
@ -40,7 +40,7 @@ function object:add_signal(name)
|
||||||
if not self._signals[name] then
|
if not self._signals[name] then
|
||||||
self._signals[name] = {
|
self._signals[name] = {
|
||||||
strong = {},
|
strong = {},
|
||||||
weak = setmetatable({}, { __mode = "k" })
|
weak = setmetatable({}, { __mode = "kv" })
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -55,6 +55,35 @@ function object:connect_signal(name, func)
|
||||||
sig.strong[func] = true
|
sig.strong[func] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function make_the_gc_obey(func)
|
||||||
|
if _VERSION <= "Lua 5.1" then
|
||||||
|
-- Lua 5.1 only has the behaviour we want if a userdata is used as the
|
||||||
|
-- value in a weak table. Thus, do some magic so that we get a userdata.
|
||||||
|
|
||||||
|
local userdata = newproxy(true)
|
||||||
|
getmetatable(userdata).__gc = function() end
|
||||||
|
-- Now bind the lifetime of userdata to the lifetime of func. For this,
|
||||||
|
-- we mess with the function's environment and add a table for all the
|
||||||
|
-- various userdata that it should keep alive.
|
||||||
|
local key = "_secret_key_used_by_gears_object_in_Lua51"
|
||||||
|
local old_env = getfenv(func)
|
||||||
|
if old_env[key] then
|
||||||
|
-- Assume the code in the else branch added this and the function
|
||||||
|
-- already has its own, private environment
|
||||||
|
table.insert(old_env[key], userdata)
|
||||||
|
else
|
||||||
|
-- No table yet, add it
|
||||||
|
local new_env = { [key] = { userdata } }
|
||||||
|
setmetatable(new_env, { __index = old_env, __newindex = old_env })
|
||||||
|
setfenv(func, new_env)
|
||||||
|
end
|
||||||
|
assert(_G[key] == nil, "Something broke, things escaped to _G")
|
||||||
|
return userdata
|
||||||
|
end
|
||||||
|
-- Lua 5.2+ already behaves the way we want with functions directly, no magic
|
||||||
|
return func
|
||||||
|
end
|
||||||
|
|
||||||
--- Connect to a signal weakly. This allows the callback function to be garbage
|
--- Connect to a signal weakly. This allows the callback function to be garbage
|
||||||
-- collected and automatically disconnects the signal when that happens.
|
-- collected and automatically disconnects the signal when that happens.
|
||||||
-- @param name The name of the signal
|
-- @param name The name of the signal
|
||||||
|
@ -63,7 +92,7 @@ function object:weak_connect_signal(name, func)
|
||||||
assert(type(func) == "function", "callback must be a function, got: " .. type(func))
|
assert(type(func) == "function", "callback must be a function, got: " .. type(func))
|
||||||
local sig = find_signal(self, name, "connect to")
|
local sig = find_signal(self, name, "connect to")
|
||||||
assert(sig.strong[func] == nil, "Trying to connect a weak callback which is already connected strongly")
|
assert(sig.strong[func] == nil, "Trying to connect a weak callback which is already connected strongly")
|
||||||
sig.weak[func] = true
|
sig.weak[func] = make_the_gc_obey(func)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Disonnect to a signal
|
--- Disonnect to a signal
|
||||||
|
|
Loading…
Reference in New Issue