[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:
Chris Wilson 2007-04-19 12:15:04 +01:00
parent dd8681b76b
commit 8ad30ccdb0
4 changed files with 204 additions and 5 deletions

View file

@ -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);
}

View file

@ -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);

View file

@ -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)
{

View file

@ -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;
}