cogl-winsys-glx: Fix frame notification race/leak

If a second `set_{sync,complete}_pending` was queued before the idle
handler had flushed the first then one of them would be forgotten.
It would stay queued forever and never emitted as a notification.

This could happen repeatedly causing a slow leak. But worse still,
`clutter-stage-cogl` would then have `pending_swaps` permanently stuck
above zero preventing the presentation timing logic from being used.

The problem is that a boolean can only count to one, but in some cases
(triple buffering, whether intentional or accidental #334) we need it to
count to two. So just change booleans to integers and count properly.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/216
This commit is contained in:
Daniel van Vugt 2018-07-23 16:28:56 +08:00
parent fa4a787386
commit 20c1295a33

View File

@ -99,9 +99,9 @@ typedef struct _CoglOnscreenGLX
CoglOnscreenXlib _parent; CoglOnscreenXlib _parent;
GLXDrawable glxwin; GLXDrawable glxwin;
uint32_t last_swap_vsync_counter; uint32_t last_swap_vsync_counter;
gboolean pending_sync_notify; uint32_t pending_sync_notify;
gboolean pending_complete_notify; uint32_t pending_complete_notify;
gboolean pending_resize_notify; uint32_t pending_resize_notify;
GThread *swap_wait_thread; GThread *swap_wait_thread;
GQueue *swap_wait_queue; GQueue *swap_wait_queue;
@ -347,35 +347,35 @@ flush_pending_notifications_cb (void *data,
{ {
CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer);
CoglOnscreenGLX *glx_onscreen = onscreen->winsys; CoglOnscreenGLX *glx_onscreen = onscreen->winsys;
gboolean pending_sync_notify = glx_onscreen->pending_sync_notify;
gboolean pending_complete_notify = glx_onscreen->pending_complete_notify;
/* If swap_region is called then notifying the sync event could while (glx_onscreen->pending_sync_notify > 0 ||
* potentially immediately queue a subsequent pending notify so glx_onscreen->pending_complete_notify > 0 ||
* we need to clear the flag before invoking the callback */ glx_onscreen->pending_resize_notify > 0)
glx_onscreen->pending_sync_notify = FALSE;
glx_onscreen->pending_complete_notify = FALSE;
if (pending_sync_notify)
{ {
CoglFrameInfo *info = g_queue_peek_head (&onscreen->pending_frame_infos); if (glx_onscreen->pending_sync_notify > 0)
{
CoglFrameInfo *info =
g_queue_peek_head (&onscreen->pending_frame_infos);
_cogl_onscreen_notify_frame_sync (onscreen, info); _cogl_onscreen_notify_frame_sync (onscreen, info);
} glx_onscreen->pending_sync_notify--;
}
if (pending_complete_notify) if (glx_onscreen->pending_complete_notify > 0)
{ {
CoglFrameInfo *info = g_queue_pop_head (&onscreen->pending_frame_infos); CoglFrameInfo *info =
g_queue_pop_head (&onscreen->pending_frame_infos);
_cogl_onscreen_notify_complete (onscreen, info); _cogl_onscreen_notify_complete (onscreen, info);
cogl_object_unref (info);
glx_onscreen->pending_complete_notify--;
}
cogl_object_unref (info); if (glx_onscreen->pending_resize_notify > 0)
} {
_cogl_onscreen_notify_resize (onscreen);
if (glx_onscreen->pending_resize_notify) glx_onscreen->pending_resize_notify--;
{ }
_cogl_onscreen_notify_resize (onscreen);
glx_onscreen->pending_resize_notify = FALSE;
} }
} }
} }
@ -417,7 +417,7 @@ set_sync_pending (CoglOnscreen *onscreen)
NULL); NULL);
} }
glx_onscreen->pending_sync_notify = TRUE; glx_onscreen->pending_sync_notify++;
} }
static void static void
@ -440,7 +440,7 @@ set_complete_pending (CoglOnscreen *onscreen)
NULL); NULL);
} }
glx_onscreen->pending_complete_notify = TRUE; glx_onscreen->pending_complete_notify++;
} }
static void static void
@ -533,7 +533,7 @@ notify_resize (CoglContext *context,
NULL); NULL);
} }
glx_onscreen->pending_resize_notify = TRUE; glx_onscreen->pending_resize_notify++;
if (!xlib_onscreen->is_foreign_xwin) if (!xlib_onscreen->is_foreign_xwin)
{ {