detached 98001e2

fix(surface): abandon a pending frame callback on close so late done and delete_id are discarded

commit
98001e29da841b7e7341285fcdbdcb7acd0c030b
author
brice · · 2026-10-01 18:59 UTC
parent
0e97e81
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
2 files · +6 -8
M surface.go +4 -4
@@ -724,9 +724,6 @@ func (s *Surface) Close() error {
724724 }
725725
726726 func (s *Surface) destroy() error {
727- // TODO(wlturbo): a frame callback pending here is unregistered locally, so
728- // its later done/delete_id reaches an unknown object. Fixing it needs
729- // server-side delete_id handling in wlturbo (see TestCloseWithPendingFrame).
730727 s.closed = true
731728 delete(s.c.surfaces, s.id)
732729 s.c.seatSurfaceGone(s)
@@ -772,7 +769,10 @@ func (s *Surface) destroy() error {
772769 add(s.surf.Destroy())
773770 }
774771 if s.cb != nil {
775- s.c.wlctx.Unregister(s.cb)
772+ // A pending frame callback is owned by the compositor: abandon it so
773+ // its late done and delete_id are discarded. An error only means no
774+ // callback was pending.
775+ _ = s.c.wlctx.Abandon(s.cb)
776776 }
777777 return errors.Join(errs...)
778778 }
@@ -724,9 +724,6 @@ func (s *Surface) Close() error {
724 }724 }
725 725
726 func (s *Surface) destroy() error {726 func (s *Surface) destroy() error {
727- // TODO(wlturbo): a frame callback pending here is unregistered locally, so
728- // its later done/delete_id reaches an unknown object. Fixing it needs
729- // server-side delete_id handling in wlturbo (see TestCloseWithPendingFrame).
730 s.closed = true727 s.closed = true
731 delete(s.c.surfaces, s.id)728 delete(s.c.surfaces, s.id)
732 s.c.seatSurfaceGone(s)729 s.c.seatSurfaceGone(s)
@@ -772,7 +769,10 @@ func (s *Surface) destroy() error {
772 add(s.surf.Destroy())769 add(s.surf.Destroy())
773 }770 }
774 if s.cb != nil {771 if s.cb != nil {
775- s.c.wlctx.Unregister(s.cb)772+ // A pending frame callback is owned by the compositor: abandon it so
773+ // its late done and delete_id are discarded. An error only means no
774+ // callback was pending.
775+ _ = s.c.wlctx.Abandon(s.cb)
776 }776 }
777 return errors.Join(errs...)777 return errors.Join(errs...)
778 }778 }
M surface_wire_test.go +2 -4
@@ -164,11 +164,9 @@ func TestLockCloseDestroysUnlockedLock(t *testing.T) {
164164 require.NoError(t, l.Close(), "idempotent")
165165 }
166166
167-// GO-002 reproduction, enabled once wlturbo handles delete_id for objects the
168-// server creates: closing a surface with a pending frame callback must not turn
169-// the compositor's later callback done and delete_id into a transport error.
167+// Closing a surface with a pending frame callback must not turn the
168+// compositor's later callback done and delete_id into a transport error.
170169 func TestCloseWithPendingFrame(t *testing.T) {
171- t.Skip("needs wlturbo server-side delete_id handling")
172170 c, srv := connectWire(t, surfaceGlobals, surfaceOutputs)
173171 s, err := c.NewToplevel("close", 64, 48)
174172 require.NoError(t, err)
@@ -164,11 +164,9 @@ func TestLockCloseDestroysUnlockedLock(t *testing.T) {
164 require.NoError(t, l.Close(), "idempotent")164 require.NoError(t, l.Close(), "idempotent")
165 }165 }
166 166
167-// GO-002 reproduction, enabled once wlturbo handles delete_id for objects the167+// Closing a surface with a pending frame callback must not turn the
168-// server creates: closing a surface with a pending frame callback must not turn168+// compositor's later callback done and delete_id into a transport error.
169-// the compositor's later callback done and delete_id into a transport error.
170 func TestCloseWithPendingFrame(t *testing.T) {169 func TestCloseWithPendingFrame(t *testing.T) {
171- t.Skip("needs wlturbo server-side delete_id handling")
172 c, srv := connectWire(t, surfaceGlobals, surfaceOutputs)170 c, srv := connectWire(t, surfaceGlobals, surfaceOutputs)
173 s, err := c.NewToplevel("close", 64, 48)171 s, err := c.NewToplevel("close", 64, 48)
174 require.NoError(t, err)172 require.NoError(t, err)

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 4

↑↓ moveenter openesc close