render/drm_syncobj: Add a callback when ready

The old approach of using a signal is fundamentally broken for a common
usecase: When the waiter is ready, it's common to immediately finish and
free any resources associated with it.
Because of the semantics of wl_signal_emit_mutable() this is UB.
wl_signal_emit_mutable() always excepts that the waiter hasn't been freed
until the signal has finished being emitted.

Instead of over engineering the solution, let's just add a callback required
by wlr_drm_syncobj_timeline_waiter_init(). In this callback, the implementation
is free to finish() or free() any resource it likes.
This commit is contained in:
Alexander Orzechowski 2025-01-26 17:35:47 -05:00
parent 211eb9d60e
commit 82223e451a
5 changed files with 21 additions and 19 deletions

View file

@ -29,7 +29,6 @@ struct wlr_linux_drm_syncobj_surface_v1_commit {
struct wlr_drm_syncobj_timeline_waiter waiter;
uint32_t cached_seq;
struct wl_listener waiter_ready;
struct wl_listener surface_destroy;
};
@ -194,14 +193,13 @@ static struct wlr_linux_drm_syncobj_surface_v1 *surface_from_wlr_surface(
static void surface_commit_destroy(struct wlr_linux_drm_syncobj_surface_v1_commit *commit) {
wlr_surface_unlock_cached(commit->surface->surface, commit->cached_seq);
wl_list_remove(&commit->surface_destroy.link);
wl_list_remove(&commit->waiter_ready.link);
wlr_drm_syncobj_timeline_waiter_finish(&commit->waiter);
free(commit);
}
static void surface_commit_handle_waiter_ready(struct wl_listener *listener, void *data) {
static void surface_commit_handle_waiter_ready(struct wlr_drm_syncobj_timeline_waiter *waiter) {
struct wlr_linux_drm_syncobj_surface_v1_commit *commit =
wl_container_of(listener, commit, waiter_ready);
wl_container_of(waiter, commit, waiter);
surface_commit_destroy(commit);
}
@ -233,7 +231,7 @@ static bool lock_surface_commit(struct wlr_linux_drm_syncobj_surface_v1 *surface
struct wl_display *display = wl_client_get_display(client);
struct wl_event_loop *loop = wl_display_get_event_loop(display);
if (!wlr_drm_syncobj_timeline_waiter_init(&commit->waiter, timeline, point,
flags, loop)) {
flags, loop, surface_commit_handle_waiter_ready)) {
free(commit);
return false;
}
@ -241,9 +239,6 @@ static bool lock_surface_commit(struct wlr_linux_drm_syncobj_surface_v1 *surface
commit->surface = surface;
commit->cached_seq = wlr_surface_lock_pending(surface->surface);
commit->waiter_ready.notify = surface_commit_handle_waiter_ready;
wl_signal_add(&commit->waiter.events.ready, &commit->waiter_ready);
commit->surface_destroy.notify = surface_commit_handle_surface_destroy;
wl_signal_add(&surface->surface->events.destroy, &commit->surface_destroy);