naughty: Never hold a strong reference to the notification in the box.

luajit was failing to GC the notification about 5% of the time. This
commit stores all widget notifications in a weak table and don't let
any lambda access the parent object notification object.

Each of those changes reduces the failure rate. There might still be
a couple in there, but the test passed 200x on my laptop with 100%
success rate.
This commit is contained in:
Emmanuel Lepage Vallee 2021-10-31 19:30:17 -07:00
parent 6e8b0d5a85
commit 9dcfde32e0
7 changed files with 105 additions and 62 deletions

View File

@ -46,22 +46,24 @@ end
-- @propemits true false
function background:set_notification(notif)
if self._private.notification == notif then return end
local old = self._private.notification[1]
if self._private.notification then
self._private.notification:disconnect_signal("property::bg",
if old == notif then return end
if old then
old:disconnect_signal("property::bg",
self._private.background_changed_callback)
self._private.notification:disconnect_signal("property::border_width",
old:disconnect_signal("property::border_width",
self._private.background_changed_callback)
self._private.notification:disconnect_signal("property::border_color",
old:disconnect_signal("property::border_color",
self._private.background_changed_callback)
self._private.notification:disconnect_signal("property::shape",
old:disconnect_signal("property::shape",
self._private.background_changed_callback)
end
update_background(notif, self)
self._private.notification = notif
self._private.notification = setmetatable({notif}, {__mode="v"})
notif:connect_signal("property::bg" , self._private.background_changed_callback)
notif:connect_signal("property::border_width", self._private.background_changed_callback)
@ -82,12 +84,13 @@ local function new(args)
args = args or {}
local bg = wbg()
bg._private.notification = {}
bg:set_border_strategy("inner")
gtable.crush(bg, background, true)
function bg._private.background_changed_callback()
update_background(bg._private.notification, bg)
update_background(bg._private.notification[1], bg)
end
if args.notification then

View File

@ -47,14 +47,16 @@ local function init_screen(s)
end
local function disconnect(self)
if self._private.notification then
self._private.notification:disconnect_signal("destroyed",
local n = self._private.notification[1]
if n then
n:disconnect_signal("destroyed",
self._private.destroy_callback)
self._private.notification:disconnect_signal("property::margin",
n:disconnect_signal("property::margin",
self._private.update)
self._private.notification:disconnect_signal("property::suspended",
n:disconnect_signal("property::suspended",
self._private.hide)
end
end
@ -102,7 +104,7 @@ local function update_position(position, preset)
honor_workarea = true,
}
if k == 1 then
args.offset = get_offset(position, preset)
args.offset = get_offset(position, preset)
end
-- The first entry is aligned to the workarea, then the following to the
@ -123,14 +125,13 @@ local function finish(self)
end
end
local preset
if self.private and self.private.args and self.private.args.notification then
preset = self.private.args.notification.preset
end
local preset = (self._private.notification[1] or {}).preset
update_position(self.position, preset)
disconnect(self)
self._private.notification = {}
end
-- It isn't a good idea to use the `attach` `awful.placement` property. If the
@ -236,21 +237,19 @@ local function generate_widget(args, n)
end
-- Call `:set_notification` on all children
awcommon._set_common_property(w, "notification", n or args.notification)
awcommon._set_common_property(w, "notification", n)
return w
end
local function init(self, notification)
local args = self._private.args
local preset = notification.preset or {}
local position = args.position or notification.position or
local position = self._private.position or notification.position or
preset.position or beautiful.notification_position or "top_right"
if not self.widget then
self.widget = generate_widget(self._private.args, notification)
self.widget = generate_widget(self._private, notification)
end
local bg = self._private.widget:get_children_by_id( "background_role" )[1]
@ -295,20 +294,26 @@ local function init(self, notification)
end
function box:set_notification(notif)
if self._private.notification == notif then return end
if self._private.notification[1] == notif then return end
disconnect(self)
init(self, notif)
self._private.notification = notif
self._private.notification = setmetatable({notif}, {__mode="v"})
self:emit_signal("property::notification", notif)
end
function box:get_notification()
return self._private.notification[1]
end
function box:get_position()
if self._private.notification then
return self._private.notification:get_position()
local n = self._private.notification[1]
if n then
return n:get_position()
end
return "top_right"
@ -357,7 +362,9 @@ local function new(args)
if not new_args.widget then return nil end
local ret = popup(new_args)
ret._private.args = new_args
ret._private.notification = {}
ret._private.widget_template = args.widget_template
ret._private.position = args.position
gtable.crush(ret, box, true)
@ -371,8 +378,10 @@ local function new(args)
--TODO remove
local function hide()
if ret._private.notification then
ret._private.notification:destroy()
local n = ret._private.notification[1]
if n then
n:destroy()
end
end

View File

@ -168,14 +168,16 @@ local function wb_label(action, self)
end
local function update(self)
if not self._private.layout or not self._private.notification then return end
local n = self._private.notification[1]
if not self._private.layout or not n then return end
awcommon.list_update(
self._private.layout,
self._private.default_buttons,
function(o) return wb_label(o, self) end,
self._private.data,
self._private.notification.actions,
n.actions,
{
widget_template = self._private.widget_template
}
@ -229,7 +231,7 @@ local actionlist = {}
function actionlist:set_notification(notif)
self._private.notification = notif
self._private.notification = setmetatable({notif}, {__mode="v"})
if not self._private.layout then
self._private.layout = wibox.layout.fixed.horizontal()
@ -329,8 +331,9 @@ local function new(_, args)
gtable.crush(wdg, actionlist, true)
wdg._private.data = {}
wdg._private.notification = {}
gtable.crush(wdg, args)
gtable.crush(wdg, args, false)
wdg._private.style = wdg._private.style or {}
@ -338,7 +341,7 @@ local function new(_, args)
wdg._private.default_buttons = gtable.join(
abutton({ }, 1, function(a)
local notif = wdg._private.notification or args.notification
local notif = wdg._private.notification[1]
a:invoke(notif)
end)
)

View File

@ -22,7 +22,7 @@ local function notif_size()
constraint:set_width(beautiful.notification_max_width or dpi(500))
rawset(constraint, "set_notification", function(_, notif)
constraint._private.notification = notif
constraint._private.notification = setmetatable({notif}, {__mode = "v"})
local s = false
if notif.width and notif.width ~= beautiful.notification_max_width then
@ -37,6 +37,10 @@ local function notif_size()
constraint.strategy = s and "exact" or "max"
end)
rawset(constraint, "get_notification", function()
return constraint._private.notification[1]
end)
return constraint
end

View File

@ -83,10 +83,12 @@ end
-- @propemits true false
function icon:set_notification(notif)
if self._private.notification == notif then return end
local old = (self._private.notification or {})[1]
if self._private.notification then
self._private.notification:disconnect_signal("destroyed",
if old == notif then return end
if old then
old:disconnect_signal("destroyed",
self._private.icon_changed_callback)
end
@ -96,7 +98,7 @@ function icon:set_notification(notif)
self:set_image(icn)
end
self._private.notification = notif
self._private.notification = setmetatable({notif}, {__mode="v"})
notif:connect_signal("property::icon", self._private.icon_changed_callback)
self:emit_signal("property::notification", notif)
@ -154,10 +156,14 @@ local function new(args)
local tb = imagebox()
gtable.crush(tb, icon, true)
tb._private.notification = {}
function tb._private.icon_changed_callback()
local n = tb._private.notification[1]
local icn = gsurface.load_silently(tb._private.notification.icon)
if not n then return end
local icn = gsurface.load_silently(n.icon)
if icn then
tb:set_image(icn)

View File

@ -27,18 +27,20 @@ local message = {}
-- @propemits true false
function message:set_notification(notif)
if self._private.notification == notif then return end
local old = self._private.notification[1]
if self._private.notification then
self._private.notification:disconnect_signal("property::message",
if old == notif then return end
if old then
old:disconnect_signal("property::message",
self._private.message_changed_callback)
self._private.notification:disconnect_signal("property::fg",
old:disconnect_signal("property::fg",
self._private.message_changed_callback)
end
markup(self, notif.message, notif.fg, notif.font)
self._private.notification = notif
self._private.notification = setmetatable({notif}, {__mode="v"})
notif:connect_signal("property::message", self._private.message_changed_callback)
notif:connect_signal("property::fg" , self._private.message_changed_callback)
@ -57,16 +59,23 @@ local function new(args)
local tb = textbox()
tb:set_wrap("word")
tb:set_font(beautiful.notification_font)
tb._private.notification = {}
gtable.crush(tb, message, true)
function tb._private.message_changed_callback()
markup(
tb,
tb._private.notification.message,
tb._private.notification.fg,
tb._private.notification.font
)
local n = tb._private.notification[1]
if n then
markup(
tb,
n.message,
n.fg,
n.font
)
else
markup(tb, nil, nil)
end
end
if args.notification then

View File

@ -27,18 +27,20 @@ local title = {}
-- @propemits true false
function title:set_notification(notif)
if self._private.notification == notif then return end
local old = self._private.notification[1]
if self._private.notification then
self._private.notification:disconnect_signal("property::message",
if old == notif then return end
if old then
old:disconnect_signal("property::message",
self._private.title_changed_callback)
self._private.notification:disconnect_signal("property::fg",
old:disconnect_signal("property::fg",
self._private.title_changed_callback)
end
markup(self, notif.title, notif.fg, notif.font)
self._private.notification = notif
self._private.notification = setmetatable({notif}, {__mode="v"})
self._private.title_changed_callback()
notif:connect_signal("property::title", self._private.title_changed_callback)
@ -58,16 +60,23 @@ local function new(args)
local tb = textbox()
tb:set_wrap("word")
tb:set_font(beautiful.notification_font)
tb._private.notification = {}
gtable.crush(tb, title, true)
function tb._private.title_changed_callback()
markup(
tb,
tb._private.notification.title,
tb._private.notification.fg,
tb._private.notification.font
)
local n = tb._private.notification[1]
if n then
markup(
tb,
n.title,
n.fg,
n.font
)
else
markup("", nil, nil)
end
end
if args.notification then