mirror of
https://gitlab.freedesktop.org/cairo/cairo.git
synced 2026-01-21 18:51:34 +01:00
[cairo-xlib] Introduce a workqueue for deferred destruction of X resources.
Due to the nature of the reference counting, an X resource may be destroyed later than anticipated and possibly from a different thread than the original context. This becomes an issue for applications that carefully manage their single X connection from a single thread and do not use locking and are then suprised when cairo triggers X traffic when performing work for a different part of the application in another thread.
This commit is contained in:
parent
dd8681b76b
commit
8ad30ccdb0
4 changed files with 204 additions and 5 deletions
|
|
@ -40,8 +40,29 @@
|
|||
#include <X11/Xlibint.h> /* For XESetCloseDisplay */
|
||||
#include <X11/extensions/Xrender.h>
|
||||
|
||||
typedef int (*cairo_xlib_error_func_t) (Display *display,
|
||||
XErrorEvent *event);
|
||||
|
||||
static cairo_xlib_display_t *_cairo_xlib_display_list = NULL;
|
||||
struct _cairo_xlib_job {
|
||||
cairo_xlib_job_t *next;
|
||||
enum {
|
||||
RESOURCE,
|
||||
WORK
|
||||
} type;
|
||||
union {
|
||||
struct {
|
||||
cairo_xlib_notify_resource_func notify;
|
||||
XID xid;
|
||||
} resource;
|
||||
struct {
|
||||
cairo_xlib_notify_func notify;
|
||||
void *data;
|
||||
void (*destroy) (void *);
|
||||
} work;
|
||||
} func;
|
||||
};
|
||||
|
||||
static cairo_xlib_display_t *_cairo_xlib_display_list;
|
||||
|
||||
static void
|
||||
_cairo_xlib_call_close_display_hooks (cairo_xlib_display_t *display)
|
||||
|
|
@ -50,6 +71,7 @@ _cairo_xlib_call_close_display_hooks (cairo_xlib_display_t *display)
|
|||
|
||||
/* call all registered shutdown routines */
|
||||
CAIRO_MUTEX_LOCK (display->mutex);
|
||||
|
||||
hooks = display->close_display_hooks;
|
||||
while (hooks != NULL) {
|
||||
display->close_display_hooks = NULL;
|
||||
|
|
@ -68,6 +90,7 @@ _cairo_xlib_call_close_display_hooks (cairo_xlib_display_t *display)
|
|||
hooks = display->close_display_hooks;
|
||||
}
|
||||
display->closed = TRUE;
|
||||
|
||||
CAIRO_MUTEX_UNLOCK (display->mutex);
|
||||
}
|
||||
|
||||
|
|
@ -97,6 +120,18 @@ _cairo_xlib_display_destroy (cairo_xlib_display_t *display)
|
|||
CAIRO_MUTEX_LOCK (display->mutex);
|
||||
assert (display->ref_count > 0);
|
||||
if (--display->ref_count == 0) {
|
||||
/* destroy all outstanding notifies */
|
||||
while (display->workqueue != NULL) {
|
||||
cairo_xlib_job_t *job = display->workqueue;
|
||||
display->workqueue = job->next;
|
||||
|
||||
if (job->type == WORK && job->func.work.destroy != NULL)
|
||||
job->func.work.destroy (job->func.work.data);
|
||||
|
||||
_cairo_freelist_free (&display->wq_freelist, job);
|
||||
}
|
||||
_cairo_freelist_fini (&display->wq_freelist);
|
||||
|
||||
CAIRO_MUTEX_UNLOCK (display->mutex);
|
||||
|
||||
free (display);
|
||||
|
|
@ -104,6 +139,12 @@ _cairo_xlib_display_destroy (cairo_xlib_display_t *display)
|
|||
CAIRO_MUTEX_UNLOCK (display->mutex);
|
||||
}
|
||||
|
||||
static int
|
||||
_noop_error_handler (Display *display,
|
||||
XErrorEvent *event)
|
||||
{
|
||||
return False; /* return value is ignored */
|
||||
}
|
||||
static int
|
||||
_cairo_xlib_close_display (Display *dpy, XExtCodes *codes)
|
||||
{
|
||||
|
|
@ -117,13 +158,26 @@ _cairo_xlib_close_display (Display *dpy, XExtCodes *codes)
|
|||
for (display = _cairo_xlib_display_list; display; display = next) {
|
||||
next = display->next;
|
||||
if (display->display == dpy) {
|
||||
cairo_xlib_error_func_t old_handler;
|
||||
|
||||
/* drop the list mutex whilst triggering the hooks */
|
||||
CAIRO_MUTEX_UNLOCK (_cairo_xlib_display_mutex);
|
||||
|
||||
/* protect the notifies from triggering XErrors */
|
||||
XSync (dpy, False);
|
||||
old_handler = XSetErrorHandler (_noop_error_handler);
|
||||
|
||||
_cairo_xlib_display_notify (display);
|
||||
_cairo_xlib_call_close_display_hooks (display);
|
||||
_cairo_xlib_display_destroy (display);
|
||||
|
||||
/* catch any that arrived before marking the display as closed */
|
||||
_cairo_xlib_display_notify (display);
|
||||
|
||||
XSync (dpy, False);
|
||||
XSetErrorHandler (old_handler);
|
||||
|
||||
CAIRO_MUTEX_LOCK (_cairo_xlib_display_mutex);
|
||||
_cairo_xlib_display_destroy (display);
|
||||
*prev = next;
|
||||
break;
|
||||
} else
|
||||
|
|
@ -185,10 +239,12 @@ _cairo_xlib_display_get (Display *dpy)
|
|||
|
||||
XESetCloseDisplay (dpy, codes->extension, _cairo_xlib_close_display);
|
||||
|
||||
_cairo_freelist_init (&display->wq_freelist, sizeof (cairo_xlib_job_t));
|
||||
display->ref_count = 2; /* add one for the CloseDisplay */
|
||||
CAIRO_MUTEX_INIT (display->mutex);
|
||||
display->display = dpy;
|
||||
display->screens = NULL;
|
||||
display->workqueue = NULL;
|
||||
display->close_display_hooks = NULL;
|
||||
display->closed = FALSE;
|
||||
|
||||
|
|
@ -256,3 +312,112 @@ _cairo_xlib_remove_close_display_hooks (Display *dpy, const void *key)
|
|||
|
||||
_cairo_xlib_display_destroy (display);
|
||||
}
|
||||
|
||||
cairo_status_t
|
||||
_cairo_xlib_display_queue_resource (cairo_xlib_display_t *display,
|
||||
cairo_xlib_notify_resource_func notify,
|
||||
XID xid)
|
||||
{
|
||||
cairo_xlib_job_t *job;
|
||||
cairo_status_t status = CAIRO_STATUS_SUCCESS;
|
||||
|
||||
job = _cairo_freelist_alloc (&display->wq_freelist);
|
||||
if (job == NULL)
|
||||
return CAIRO_STATUS_NO_MEMORY;
|
||||
|
||||
job->type = RESOURCE;
|
||||
job->func.resource.xid = xid;
|
||||
job->func.resource.notify = notify;
|
||||
|
||||
CAIRO_MUTEX_LOCK (display->mutex);
|
||||
if (display->closed == FALSE) {
|
||||
job->next = display->workqueue;
|
||||
display->workqueue = job;
|
||||
} else {
|
||||
_cairo_freelist_free (&display->wq_freelist, job);
|
||||
job = NULL;
|
||||
status = CAIRO_STATUS_NO_MEMORY;
|
||||
}
|
||||
CAIRO_MUTEX_UNLOCK (display->mutex);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
cairo_status_t
|
||||
_cairo_xlib_display_queue_work (cairo_xlib_display_t *display,
|
||||
cairo_xlib_notify_func notify,
|
||||
void *data,
|
||||
void (*destroy) (void *))
|
||||
{
|
||||
cairo_xlib_job_t *job;
|
||||
cairo_status_t status = CAIRO_STATUS_SUCCESS;
|
||||
|
||||
job = _cairo_freelist_alloc (&display->wq_freelist);
|
||||
if (job == NULL)
|
||||
return CAIRO_STATUS_NO_MEMORY;
|
||||
|
||||
job->type = WORK;
|
||||
job->func.work.data = data;
|
||||
job->func.work.notify = notify;
|
||||
job->func.work.destroy = destroy;
|
||||
|
||||
CAIRO_MUTEX_LOCK (display->mutex);
|
||||
if (display->closed == FALSE) {
|
||||
job->next = display->workqueue;
|
||||
display->workqueue = job;
|
||||
} else {
|
||||
_cairo_freelist_free (&display->wq_freelist, job);
|
||||
job = NULL;
|
||||
status = CAIRO_STATUS_NO_MEMORY;
|
||||
}
|
||||
CAIRO_MUTEX_UNLOCK (display->mutex);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
void
|
||||
_cairo_xlib_display_notify (cairo_xlib_display_t *display)
|
||||
{
|
||||
cairo_xlib_job_t *jobs, *job;
|
||||
|
||||
CAIRO_MUTEX_LOCK (display->mutex);
|
||||
jobs = display->workqueue;
|
||||
while (jobs != NULL) {
|
||||
display->workqueue = NULL;
|
||||
CAIRO_MUTEX_UNLOCK (display->mutex);
|
||||
|
||||
/* reverse the list to obtain FIFO order */
|
||||
job = NULL;
|
||||
do {
|
||||
cairo_xlib_job_t *next = jobs->next;
|
||||
jobs->next = job;
|
||||
job = jobs;
|
||||
jobs = next;
|
||||
} while (jobs != NULL);
|
||||
jobs = job;
|
||||
|
||||
do {
|
||||
job = jobs;
|
||||
jobs = job->next;
|
||||
|
||||
switch (job->type){
|
||||
case WORK:
|
||||
job->func.work.notify (display->display, job->func.work.data);
|
||||
if (job->func.work.destroy != NULL)
|
||||
job->func.work.destroy (job->func.work.data);
|
||||
break;
|
||||
|
||||
case RESOURCE:
|
||||
job->func.resource.notify (display->display,
|
||||
job->func.resource.xid);
|
||||
break;
|
||||
}
|
||||
|
||||
_cairo_freelist_free (&display->wq_freelist, job);
|
||||
} while (jobs != NULL);
|
||||
|
||||
CAIRO_MUTEX_LOCK (display->mutex);
|
||||
jobs = display->workqueue;
|
||||
}
|
||||
CAIRO_MUTEX_UNLOCK (display->mutex);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,9 +35,13 @@
|
|||
|
||||
#include "cairoint.h"
|
||||
#include "cairo-xlib.h"
|
||||
#include "cairo-freelist-private.h"
|
||||
|
||||
typedef struct _cairo_xlib_display cairo_xlib_display_t;
|
||||
typedef struct _cairo_xlib_hook cairo_xlib_hook_t;
|
||||
typedef struct _cairo_xlib_job cairo_xlib_job_t;
|
||||
typedef void (*cairo_xlib_notify_func) (Display *, void *);
|
||||
typedef void (*cairo_xlib_notify_resource_func) (Display *, XID);
|
||||
|
||||
struct _cairo_xlib_hook {
|
||||
cairo_xlib_hook_t *next;
|
||||
|
|
@ -54,6 +58,9 @@ struct _cairo_xlib_display {
|
|||
Display *display;
|
||||
cairo_xlib_screen_info_t *screens;
|
||||
|
||||
cairo_xlib_job_t *workqueue;
|
||||
cairo_freelist_t wq_freelist;
|
||||
|
||||
cairo_xlib_hook_t *close_display_hooks;
|
||||
unsigned int closed :1;
|
||||
};
|
||||
|
|
@ -82,6 +89,17 @@ _cairo_xlib_add_close_display_hook (Display *display, void (*func) (Display *, v
|
|||
cairo_private void
|
||||
_cairo_xlib_remove_close_display_hooks (Display *display, const void *key);
|
||||
|
||||
cairo_private cairo_status_t
|
||||
_cairo_xlib_display_queue_work (cairo_xlib_display_t *display,
|
||||
cairo_xlib_notify_func notify,
|
||||
void *data,
|
||||
void (*destroy)(void *));
|
||||
cairo_private cairo_status_t
|
||||
_cairo_xlib_display_queue_resource (cairo_xlib_display_t *display,
|
||||
cairo_xlib_notify_resource_func notify,
|
||||
XID resource);
|
||||
cairo_private void
|
||||
_cairo_xlib_display_notify (cairo_xlib_display_t *display);
|
||||
|
||||
cairo_private cairo_xlib_screen_info_t *
|
||||
_cairo_xlib_screen_info_get (Display *display, Screen *screen);
|
||||
|
|
|
|||
|
|
@ -242,7 +242,6 @@ _cairo_xlib_init_screen_font_options (Display *dpy, cairo_xlib_screen_info_t *in
|
|||
cairo_font_options_set_hint_metrics (&info->font_options, CAIRO_HINT_METRICS_ON);
|
||||
}
|
||||
|
||||
|
||||
cairo_xlib_screen_info_t *
|
||||
_cairo_xlib_screen_info_reference (cairo_xlib_screen_info_t *info)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -220,6 +220,8 @@ _cairo_xlib_surface_create_similar (void *abstract_src,
|
|||
cairo_xlib_surface_t *surface;
|
||||
Pixmap pix;
|
||||
|
||||
_cairo_xlib_display_notify (src->screen_info->display);
|
||||
|
||||
/* Start by examining the surface's XRenderFormat, or if it
|
||||
* doesn't have one, then look one up through its visual (in the
|
||||
* case of a bitmap, it won't even have that). */
|
||||
|
|
@ -754,6 +756,8 @@ _cairo_xlib_surface_acquire_source_image (void *abstract_surf
|
|||
cairo_image_surface_t *image;
|
||||
cairo_status_t status;
|
||||
|
||||
_cairo_xlib_display_notify (surface->screen_info->display);
|
||||
|
||||
status = _get_image_surface (surface, NULL, &image, NULL);
|
||||
if (status)
|
||||
return status;
|
||||
|
|
@ -783,6 +787,8 @@ _cairo_xlib_surface_acquire_dest_image (void *abstract_surfac
|
|||
cairo_image_surface_t *image;
|
||||
cairo_status_t status;
|
||||
|
||||
_cairo_xlib_display_notify (surface->screen_info->display);
|
||||
|
||||
status = _get_image_surface (surface, interest_rect, &image, image_rect_out);
|
||||
if (status)
|
||||
return status;
|
||||
|
|
@ -834,6 +840,8 @@ _cairo_xlib_surface_clone_similar (void *abstract_surface,
|
|||
cairo_xlib_surface_t *clone;
|
||||
cairo_status_t status;
|
||||
|
||||
_cairo_xlib_display_notify (surface->screen_info->display);
|
||||
|
||||
if (src->backend == surface->base.backend ) {
|
||||
cairo_xlib_surface_t *xlib_src = (cairo_xlib_surface_t *)src;
|
||||
|
||||
|
|
@ -1268,6 +1276,8 @@ _cairo_xlib_surface_composite (cairo_operator_t op,
|
|||
int itx, ity;
|
||||
cairo_bool_t is_integer_translation;
|
||||
|
||||
_cairo_xlib_display_notify (dst->screen_info->display);
|
||||
|
||||
if (!CAIRO_SURFACE_RENDER_HAS_COMPOSITE (dst))
|
||||
return CAIRO_INT_STATUS_UNSUPPORTED;
|
||||
|
||||
|
|
@ -1417,6 +1427,8 @@ _cairo_xlib_surface_fill_rectangles (void *abstract_surface,
|
|||
cairo_xlib_surface_t *surface = abstract_surface;
|
||||
XRenderColor render_color;
|
||||
|
||||
_cairo_xlib_display_notify (surface->screen_info->display);
|
||||
|
||||
if (!CAIRO_SURFACE_RENDER_HAS_FILL_RECTANGLE (surface))
|
||||
return CAIRO_INT_STATUS_UNSUPPORTED;
|
||||
|
||||
|
|
@ -1549,6 +1561,8 @@ _cairo_xlib_surface_composite_trapezoids (cairo_operator_t op,
|
|||
int render_src_x, render_src_y;
|
||||
XRenderPictFormat *pict_format;
|
||||
|
||||
_cairo_xlib_display_notify (dst->screen_info->display);
|
||||
|
||||
if (!CAIRO_SURFACE_RENDER_HAS_TRAPEZOIDS (dst))
|
||||
return CAIRO_INT_STATUS_UNSUPPORTED;
|
||||
|
||||
|
|
@ -1661,6 +1675,8 @@ _cairo_xlib_surface_set_clip_region (void *abstract_surface,
|
|||
if (surface->have_clip_rects == FALSE && region == NULL)
|
||||
return CAIRO_STATUS_SUCCESS;
|
||||
|
||||
_cairo_xlib_display_notify (surface->screen_info->display);
|
||||
|
||||
if (surface->clip_rects != surface->embedded_clip_rects) {
|
||||
free (surface->clip_rects);
|
||||
surface->clip_rects = surface->embedded_clip_rects;
|
||||
|
|
@ -2266,7 +2282,7 @@ typedef struct _cairo_xlib_surface_font_private {
|
|||
|
||||
static void
|
||||
_cairo_xlib_surface_remove_scaled_font (Display *dpy,
|
||||
void *data)
|
||||
void *data)
|
||||
{
|
||||
cairo_scaled_font_t *scaled_font = data;
|
||||
cairo_xlib_surface_font_private_t *font_private;
|
||||
|
|
@ -2278,7 +2294,7 @@ _cairo_xlib_surface_remove_scaled_font (Display *dpy,
|
|||
_cairo_scaled_font_reset_cache (scaled_font);
|
||||
CAIRO_MUTEX_UNLOCK (scaled_font->mutex);
|
||||
|
||||
if (font_private) {
|
||||
if (font_private != NULL) {
|
||||
XRenderFreeGlyphSet (font_private->dpy, font_private->glyphset);
|
||||
free (font_private);
|
||||
}
|
||||
|
|
@ -2947,6 +2963,7 @@ _cairo_xlib_surface_show_glyphs (void *abstract_dst,
|
|||
_cairo_pattern_fini (&solid_pattern.base);
|
||||
BAIL0:
|
||||
_cairo_scaled_font_thaw_cache (scaled_font);
|
||||
_cairo_xlib_display_notify (dst->screen_info->display);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue