[PATCH] [cairo-atomic] Rewrite reference counting using atomic ops.

Chris Wilson chris at chris-wilson.co.uk
Sun Sep 23 13:08:09 PDT 2007


Introduce an opaque cairo_reference_count_t and define operations on it
in terms of atomic ops. Update all users of reference counters to use
the new opaque type.
---
 src/Makefile.am                     |    1 +
 src/cairo-clip-private.h            |   12 +++---
 src/cairo-clip.c                    |   11 +++--
 src/cairo-font-face.c               |   49 ++++++++++------------
 src/cairo-ft-font.c                 |    2 +-
 src/cairo-image-surface.c           |    2 +-
 src/cairo-pattern.c                 |   50 ++++++++++++-----------
 src/cairo-private.h                 |    3 +-
 src/cairo-reference-count-private.h |   66 ++++++++++++++++++++++++++++++
 src/cairo-scaled-font-private.h     |    3 +-
 src/cairo-scaled-font.c             |   77 +++++++++++++++++------------------
 src/cairo-surface-private.h         |    3 +-
 src/cairo-surface.c                 |   33 ++++++++-------
 src/cairo-xlib-display.c            |   42 ++++++++-----------
 src/cairo-xlib-private.h            |    5 +-
 src/cairo-xlib-screen.c             |   12 +++--
 src/cairo.c                         |   23 +++++-----
 src/cairoint.h                      |   27 ++++++------
 18 files changed, 244 insertions(+), 177 deletions(-)

diff --git a/src/Makefile.am b/src/Makefile.am
index 2e6d2c6..0564d99 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -209,6 +209,7 @@ libcairo_la_base_sources =			\
 	cairo-pen.c				\
 	cairo-polygon.c				\
 	cairo-rectangle.c			\
+	cairo-reference-count-private.h		\
 	cairo-region.c				\
 	cairo-scaled-font.c			\
 	cairo-scaled-font-private.h		\
diff --git a/src/cairo-clip-private.h b/src/cairo-clip-private.h
index 7f880d8..c2fe494 100644
--- a/src/cairo-clip-private.h
+++ b/src/cairo-clip-private.h
@@ -42,12 +42,12 @@
 extern const cairo_private cairo_rectangle_list_t _cairo_rectangles_nil;
 
 struct _cairo_clip_path {
-    unsigned int	ref_count;
-    cairo_path_fixed_t	path;
-    cairo_fill_rule_t	fill_rule;
-    double		tolerance;
-    cairo_antialias_t	antialias;
-    cairo_clip_path_t	*prev;
+    cairo_reference_count_t	 ref_count;
+    cairo_path_fixed_t		 path;
+    cairo_fill_rule_t		 fill_rule;
+    double			 tolerance;
+    cairo_antialias_t		 antialias;
+    cairo_clip_path_t		*prev;
 };
 
 struct _cairo_clip {
diff --git a/src/cairo-clip.c b/src/cairo-clip.c
index 214b408..c1667d2 100644
--- a/src/cairo-clip.c
+++ b/src/cairo-clip.c
@@ -285,7 +285,7 @@ _cairo_clip_intersect_path (cairo_clip_t       *clip,
 	return status;
     }
 
-    clip_path->ref_count = 1;
+    CAIRO_REFERENCE_COUNT_INIT (&clip_path->ref_count, 1);
     clip_path->fill_rule = fill_rule;
     clip_path->tolerance = tolerance;
     clip_path->antialias = antialias;
@@ -301,7 +301,9 @@ _cairo_clip_path_reference (cairo_clip_path_t *clip_path)
     if (clip_path == NULL)
 	return NULL;
 
-    clip_path->ref_count++;
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&clip_path->ref_count));
+
+    _cairo_reference_count_inc (&clip_path->ref_count);
 
     return clip_path;
 }
@@ -312,8 +314,9 @@ _cairo_clip_path_destroy (cairo_clip_path_t *clip_path)
     if (clip_path == NULL)
 	return;
 
-    clip_path->ref_count--;
-    if (clip_path->ref_count)
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&clip_path->ref_count));
+
+    if (! _cairo_reference_count_dec_and_test (&clip_path->ref_count))
 	return;
 
     _cairo_path_fixed_fini (&clip_path->path);
diff --git a/src/cairo-font-face.c b/src/cairo-font-face.c
index 3b6dab3..c648020 100644
--- a/src/cairo-font-face.c
+++ b/src/cairo-font-face.c
@@ -50,7 +50,7 @@ static const cairo_font_face_backend_t _cairo_toy_font_face_backend;
 const cairo_font_face_t _cairo_font_face_nil = {
     { 0 },			/* hash_entry */
     CAIRO_STATUS_NO_MEMORY,	/* status */
-    CAIRO_REF_COUNT_INVALID,	/* ref_count */
+    CAIRO_REFERENCE_COUNT_INVALID,	/* ref_count */
     { 0, 0, 0, NULL },		/* user_data */
     &_cairo_toy_font_face_backend
 };
@@ -62,7 +62,7 @@ _cairo_font_face_init (cairo_font_face_t               *font_face,
     CAIRO_MUTEX_INITIALIZE ();
 
     font_face->status = CAIRO_STATUS_SUCCESS;
-    font_face->ref_count = 1;
+    CAIRO_REFERENCE_COUNT_INIT (&font_face->ref_count, 1);
     font_face->backend = backend;
 
     _cairo_user_data_array_init (&font_face->user_data);
@@ -85,18 +85,15 @@ _cairo_font_face_init (cairo_font_face_t               *font_face,
 cairo_font_face_t *
 cairo_font_face_reference (cairo_font_face_t *font_face)
 {
-    if (font_face == NULL || font_face->ref_count == CAIRO_REF_COUNT_INVALID)
+    if (font_face == NULL ||
+	    CAIRO_REFERENCE_COUNT_IS_INVALID (&font_face->ref_count))
 	return font_face;
 
-    CAIRO_MUTEX_LOCK (_cairo_font_face_mutex);
-
-    /* We would normally assert (font_face->ref_count >0) here but we
+    /* We would normally assert that we have a reference here but we
      * can't get away with that due to the zombie case as documented
      * in _cairo_ft_font_face_destroy. */
 
-    font_face->ref_count++;
-
-    CAIRO_MUTEX_UNLOCK (_cairo_font_face_mutex);
+    _cairo_reference_count_inc (&font_face->ref_count);
 
     return font_face;
 }
@@ -113,19 +110,14 @@ slim_hidden_def (cairo_font_face_reference);
 void
 cairo_font_face_destroy (cairo_font_face_t *font_face)
 {
-    if (font_face == NULL || font_face->ref_count == CAIRO_REF_COUNT_INVALID)
+    if (font_face == NULL ||
+	    CAIRO_REFERENCE_COUNT_IS_INVALID (&font_face->ref_count))
 	return;
 
-    CAIRO_MUTEX_LOCK (_cairo_font_face_mutex);
-
-    assert (font_face->ref_count > 0);
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&font_face->ref_count));
 
-    if (--(font_face->ref_count) > 0) {
-        CAIRO_MUTEX_UNLOCK (_cairo_font_face_mutex);
+    if (! _cairo_reference_count_dec_and_test (&font_face->ref_count))
 	return;
-    }
-
-    CAIRO_MUTEX_UNLOCK (_cairo_font_face_mutex);
 
     font_face->backend->destroy (font_face);
 
@@ -133,7 +125,7 @@ cairo_font_face_destroy (cairo_font_face_t *font_face)
      * FreeType backend where cairo_ft_font_face_t and cairo_ft_unscaled_font_t
      * need to effectively mutually reference each other
      */
-    if (font_face->ref_count > 0)
+    if (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&font_face->ref_count))
 	return;
 
     _cairo_user_data_array_fini (&font_face->user_data);
@@ -173,10 +165,11 @@ cairo_font_face_get_type (cairo_font_face_t *font_face)
 unsigned int
 cairo_font_face_get_reference_count (cairo_font_face_t *font_face)
 {
-    if (font_face == NULL || font_face->ref_count == CAIRO_REF_COUNT_INVALID)
+    if (font_face == NULL ||
+	    CAIRO_REFERENCE_COUNT_IS_INVALID (&font_face->ref_count))
 	return 0;
 
-    return font_face->ref_count;
+    return CAIRO_REFERENCE_COUNT_GET_VALUE (&font_face->ref_count);
 }
 
 /**
@@ -237,7 +230,7 @@ cairo_font_face_set_user_data (cairo_font_face_t	   *font_face,
 			       void			   *user_data,
 			       cairo_destroy_func_t	    destroy)
 {
-    if (font_face->ref_count == CAIRO_REF_COUNT_INVALID)
+    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&font_face->ref_count))
 	return CAIRO_STATUS_NO_MEMORY;
 
     return _cairo_user_data_array_set_data (&font_face->user_data,
@@ -395,7 +388,7 @@ _cairo_toy_font_face_create (const char          *family,
     {
 	/* We increment the reference count here manually to avoid
 	   double-locking. */
-	font_face->base.ref_count++;
+	_cairo_reference_count_inc (&font_face->base.ref_count);
 	_cairo_toy_font_face_hash_table_unlock ();
 	return &font_face->base;
     }
@@ -475,7 +468,7 @@ void
 _cairo_unscaled_font_init (cairo_unscaled_font_t               *unscaled_font,
 			   const cairo_unscaled_font_backend_t *backend)
 {
-    unscaled_font->ref_count = 1;
+    CAIRO_REFERENCE_COUNT_INIT (&unscaled_font->ref_count, 1);
     unscaled_font->backend = backend;
 }
 
@@ -485,7 +478,9 @@ _cairo_unscaled_font_reference (cairo_unscaled_font_t *unscaled_font)
     if (unscaled_font == NULL)
 	return NULL;
 
-    unscaled_font->ref_count++;
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&unscaled_font->ref_count));
+
+    _cairo_reference_count_inc (&unscaled_font->ref_count);
 
     return unscaled_font;
 }
@@ -496,7 +491,9 @@ _cairo_unscaled_font_destroy (cairo_unscaled_font_t *unscaled_font)
     if (unscaled_font == NULL)
 	return;
 
-    if (--(unscaled_font->ref_count) > 0)
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&unscaled_font->ref_count));
+
+    if (! _cairo_reference_count_dec_and_test (&unscaled_font->ref_count))
 	return;
 
     unscaled_font->backend->destroy (unscaled_font);
diff --git a/src/cairo-ft-font.c b/src/cairo-ft-font.c
index 0161dae..3262163 100644
--- a/src/cairo-ft-font.c
+++ b/src/cairo-ft-font.c
@@ -2198,7 +2198,7 @@ _cairo_ft_font_face_destroy (void *abstract_face)
 
     if (font_face->unscaled &&
 	font_face->unscaled->from_face &&
-	font_face->unscaled->base.ref_count > 1)
+	CAIRO_REFERENCE_COUNT_GET_VALUE (&font_face->unscaled->base.ref_count) > 1)
     {
 	cairo_font_face_reference (&font_face->base);
 
diff --git a/src/cairo-image-surface.c b/src/cairo-image-surface.c
index 04d8452..24c83ec 100644
--- a/src/cairo-image-surface.c
+++ b/src/cairo-image-surface.c
@@ -42,7 +42,7 @@ static const cairo_image_surface_t _cairo_image_surface_nil_invalid_format = {
 	&cairo_image_surface_backend,	/* backend */
 	CAIRO_SURFACE_TYPE_IMAGE,
 	CAIRO_CONTENT_COLOR,
-	CAIRO_REF_COUNT_INVALID,	/* ref_count */
+	CAIRO_REFERENCE_COUNT_INVALID,	/* ref_count */
 	CAIRO_STATUS_INVALID_FORMAT,	/* status */
 	FALSE,				/* finished */
 	{ 0,	/* size */
diff --git a/src/cairo-pattern.c b/src/cairo-pattern.c
index b75040b..231f5b2 100644
--- a/src/cairo-pattern.c
+++ b/src/cairo-pattern.c
@@ -32,31 +32,31 @@
 
 const cairo_solid_pattern_t _cairo_pattern_nil = {
     { CAIRO_PATTERN_TYPE_SOLID, 	/* type */
-      CAIRO_REF_COUNT_INVALID,		/* ref_count */
-      CAIRO_STATUS_NO_MEMORY,	/* status */
+      CAIRO_REFERENCE_COUNT_INVALID,	/* ref_count */
+      CAIRO_STATUS_NO_MEMORY,		/* status */
       { 0, 0, 0, NULL },		/* user_data */
-      { 1., 0., 0., 1., 0., 0., }, /* matrix */
-      CAIRO_FILTER_DEFAULT,	/* filter */
+      { 1., 0., 0., 1., 0., 0., },	/* matrix */
+      CAIRO_FILTER_DEFAULT,		/* filter */
       CAIRO_EXTEND_GRADIENT_DEFAULT },	/* extend */
 };
 
 static const cairo_solid_pattern_t _cairo_pattern_nil_null_pointer = {
     { CAIRO_PATTERN_TYPE_SOLID, 	/* type */
-      CAIRO_REF_COUNT_INVALID,		/* ref_count */
-      CAIRO_STATUS_NULL_POINTER,/* status */
+      CAIRO_REFERENCE_COUNT_INVALID,	/* ref_count */
+      CAIRO_STATUS_NULL_POINTER,	/* status */
       { 0, 0, 0, NULL },		/* user_data */
-      { 1., 0., 0., 1., 0., 0., }, /* matrix */
-      CAIRO_FILTER_DEFAULT,	/* filter */
+      { 1., 0., 0., 1., 0., 0., },	/* matrix */
+      CAIRO_FILTER_DEFAULT,		/* filter */
       CAIRO_EXTEND_GRADIENT_DEFAULT },	/* extend */
 };
 
 const cairo_solid_pattern_t cairo_pattern_none = {
     { CAIRO_PATTERN_TYPE_SOLID, 	/* type */
-      CAIRO_REF_COUNT_INVALID,		/* ref_count */
+      CAIRO_REFERENCE_COUNT_INVALID,	/* ref_count */
       CAIRO_STATUS_SUCCESS,		/* status */
       { 0, 0, 0, NULL },		/* user_data */
-      { 1., 0., 0., 1., 0., 0., }, /* matrix */
-      CAIRO_FILTER_DEFAULT,	/* filter */
+      { 1., 0., 0., 1., 0., 0., },	/* matrix */
+      CAIRO_FILTER_DEFAULT,		/* filter */
       CAIRO_EXTEND_GRADIENT_DEFAULT },	/* extend */
 };
 
@@ -95,7 +95,7 @@ _cairo_pattern_init (cairo_pattern_t *pattern, cairo_pattern_type_t type)
     CAIRO_MUTEX_INITIALIZE ();
 
     pattern->type      = type;
-    pattern->ref_count = 1;
+    CAIRO_REFERENCE_COUNT_INIT (&pattern->ref_count, 1);
     pattern->status    = CAIRO_STATUS_SUCCESS;
 
     _cairo_user_data_array_init (&pattern->user_data);
@@ -186,7 +186,7 @@ _cairo_pattern_init_copy (cairo_pattern_t	*pattern,
     }
 
     /* The reference count and user_data array are unique to the copy. */
-    pattern->ref_count = 1;
+    CAIRO_REFERENCE_COUNT_INIT (&pattern->ref_count, 1);
     _cairo_user_data_array_init (&pattern->user_data);
 
     return CAIRO_STATUS_SUCCESS;
@@ -577,12 +577,13 @@ cairo_pattern_create_radial (double cx0, double cy0, double radius0,
 cairo_pattern_t *
 cairo_pattern_reference (cairo_pattern_t *pattern)
 {
-    if (pattern == NULL || pattern->ref_count == CAIRO_REF_COUNT_INVALID)
+    if (pattern == NULL ||
+	    CAIRO_REFERENCE_COUNT_IS_INVALID (&pattern->ref_count))
 	return pattern;
 
-    assert (pattern->ref_count > 0);
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&pattern->ref_count));
 
-    pattern->ref_count++;
+    _cairo_reference_count_inc (&pattern->ref_count);
 
     return pattern;
 }
@@ -634,13 +635,13 @@ slim_hidden_def (cairo_pattern_status);
 void
 cairo_pattern_destroy (cairo_pattern_t *pattern)
 {
-    if (pattern == NULL || pattern->ref_count == CAIRO_REF_COUNT_INVALID)
+    if (pattern == NULL ||
+	    CAIRO_REFERENCE_COUNT_IS_INVALID (&pattern->ref_count))
 	return;
 
-    assert (pattern->ref_count > 0);
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&pattern->ref_count));
 
-    pattern->ref_count--;
-    if (pattern->ref_count)
+    if (! _cairo_reference_count_dec_and_test (&pattern->ref_count))
 	return;
 
     _cairo_pattern_fini (pattern);
@@ -680,10 +681,11 @@ slim_hidden_def (cairo_pattern_destroy);
 unsigned int
 cairo_pattern_get_reference_count (cairo_pattern_t *pattern)
 {
-    if (pattern == NULL || pattern->ref_count == CAIRO_REF_COUNT_INVALID)
+    if (pattern == NULL ||
+	    CAIRO_REFERENCE_COUNT_IS_INVALID (&pattern->ref_count))
 	return 0;
 
-    return pattern->ref_count;
+    return CAIRO_REFERENCE_COUNT_GET_VALUE (&pattern->ref_count);
 }
 
 /**
@@ -732,7 +734,7 @@ cairo_pattern_set_user_data (cairo_pattern_t		 *pattern,
 			     void			 *user_data,
 			     cairo_destroy_func_t	  destroy)
 {
-    if (pattern->ref_count == CAIRO_REF_COUNT_INVALID)
+    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&pattern->ref_count))
 	return CAIRO_STATUS_NO_MEMORY;
 
     return _cairo_user_data_array_set_data (&pattern->user_data,
@@ -1328,7 +1330,7 @@ _cairo_pattern_solid_surface_matches (
 	const cairo_solid_pattern_t			    *pattern,
 	cairo_surface_t					    *dst)
 {
-    if (cache->surface->ref_count != 1)
+    if (CAIRO_REFERENCE_COUNT_GET_VALUE (&cache->surface->ref_count) != 1)
 	return FALSE;
 
     if (! _cairo_color_equal (&cache->color, &pattern->color))
diff --git a/src/cairo-private.h b/src/cairo-private.h
index 14cc79a..a5faec8 100644
--- a/src/cairo-private.h
+++ b/src/cairo-private.h
@@ -36,11 +36,12 @@
 #ifndef CAIRO_PRIVATE_H
 #define CAIRO_PRIVATE_H
 
+#include "cairo-reference-count-private.h"
 #include "cairo-gstate-private.h"
 #include "cairo-path-fixed-private.h"
 
 struct _cairo {
-    unsigned int ref_count;
+    cairo_reference_count_t ref_count;
 
     cairo_status_t status;
 
diff --git a/src/cairo-reference-count-private.h b/src/cairo-reference-count-private.h
new file mode 100644
index 0000000..2d1d81b
--- /dev/null
+++ b/src/cairo-reference-count-private.h
@@ -0,0 +1,66 @@
+/* cairo - a vector graphics library with display and print output
+ *
+ * Copyright © 2007 Chris Wilson
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it either under the terms of the GNU Lesser General Public
+ * License version 2.1 as published by the Free Software Foundation
+ * (the "LGPL") or, at your option, under the terms of the Mozilla
+ * Public License Version 1.1 (the "MPL"). If you do not alter this
+ * notice, a recipient may use your version of this file under either
+ * the MPL or the LGPL.
+ *
+ * You should have received a copy of the LGPL along with this library
+ * in the file COPYING-LGPL-2.1; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * You should have received a copy of the MPL along with this library
+ * in the file COPYING-MPL-1.1
+ *
+ * The contents of this file are subject to the Mozilla Public License
+ * Version 1.1 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
+ * OF ANY KIND, either express or implied. See the LGPL or the MPL for
+ * the specific language governing rights and limitations.
+ *
+ * The Original Code is the cairo graphics library.
+ *
+ * The Initial Developer of the Original Code is University of Southern
+ * California.
+ *
+ * Contributor(s):
+ *	Chris Wilson <chris at chris-wilson.co.uk>
+ */
+
+#ifndef CAIRO_REFRENCE_COUNT_PRIVATE_H
+#define CAIRO_REFRENCE_COUNT_PRIVATE_H
+
+#include "cairo-atomic-private.h"
+
+CAIRO_BEGIN_DECLS
+
+/* Encapsulate operations on the object's reference count */
+typedef struct {
+    cairo_atomic_int_t ref_count;
+} cairo_reference_count_t;
+
+#define _cairo_reference_count_inc(RC) _cairo_atomic_int_inc (&(RC)->ref_count)
+#define _cairo_reference_count_dec_and_test(RC) _cairo_atomic_int_dec_and_test (&(RC)->ref_count)
+
+#define CAIRO_REFERENCE_COUNT_INIT(RC, VALUE) ((RC)->ref_count = (VALUE))
+
+#define CAIRO_REFERENCE_COUNT_GET_VALUE(RC) _cairo_atomic_int_get (&(RC)->ref_count)
+#define CAIRO_REFERENCE_COUNT_SET_VALUE(RC, VALUE) _cairo_atomic_int_set (&(RC)->ref_count, (VALUE))
+
+#define CAIRO_REFERENCE_COUNT_INVALID_VALUE ((cairo_atomic_int_t) -1)
+#define CAIRO_REFERENCE_COUNT_INVALID ((cairo_reference_count_t) {CAIRO_REFERENCE_COUNT_INVALID_VALUE})
+
+#define CAIRO_REFERENCE_COUNT_IS_INVALID(RC) (CAIRO_REFERENCE_COUNT_GET_VALUE (RC) == CAIRO_REFERENCE_COUNT_INVALID_VALUE)
+
+#define CAIRO_REFERENCE_COUNT_HAS_REFERENCE(RC) (CAIRO_REFERENCE_COUNT_GET_VALUE (RC) > 0)
+
+CAIRO_END_DECLS
+
+#endif
diff --git a/src/cairo-scaled-font-private.h b/src/cairo-scaled-font-private.h
index fa71644..f16487c 100644
--- a/src/cairo-scaled-font-private.h
+++ b/src/cairo-scaled-font-private.h
@@ -42,6 +42,7 @@
 
 #include "cairo-types-private.h"
 #include "cairo-mutex-type-private.h"
+#include "cairo-reference-count-private.h"
 
 struct _cairo_scaled_font {
     /* For most cairo objects, the rule for multiple threads is that
@@ -79,7 +80,7 @@ struct _cairo_scaled_font {
 
     /* useful bits for _cairo_scaled_font_nil */
     cairo_status_t status;
-    unsigned int ref_count;
+    cairo_reference_count_t ref_count;
     cairo_user_data_array_t user_data;
 
     /* hash key members */
diff --git a/src/cairo-scaled-font.c b/src/cairo-scaled-font.c
index cb3af9f..f3808c9 100644
--- a/src/cairo-scaled-font.c
+++ b/src/cairo-scaled-font.c
@@ -180,7 +180,7 @@ _cairo_scaled_glyph_destroy (void *abstract_glyph)
 const cairo_scaled_font_t _cairo_scaled_font_nil = {
     { 0 },			/* hash_entry */
     CAIRO_STATUS_NO_MEMORY,	/* status */
-    CAIRO_REF_COUNT_INVALID,	/* ref_count */
+    CAIRO_REFERENCE_COUNT_INVALID,	/* ref_count */
     { 0, 0, 0, NULL },		/* user_data */
     NULL,			/* font_face */
     { 1., 0., 0., 1., 0, 0},	/* font_matrix */
@@ -241,7 +241,7 @@ _cairo_scaled_font_set_error (cairo_scaled_font_t *scaled_font,
 cairo_font_type_t
 cairo_scaled_font_get_type (cairo_scaled_font_t *scaled_font)
 {
-    if (scaled_font->ref_count == CAIRO_REF_COUNT_INVALID)
+    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count))
 	return CAIRO_FONT_TYPE_TOY;
 
     return scaled_font->backend->type;
@@ -352,7 +352,7 @@ _cairo_scaled_font_map_destroy (void)
 	/* We should only get here through the reset_static_data path
 	 * and there had better not be any active references at that
 	 * point. */
-	assert (scaled_font->ref_count == 0);
+	assert (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count));
 	_cairo_hash_table_remove (font_map->hash_table,
 				  &scaled_font->hash_entry);
 	_cairo_scaled_font_fini (scaled_font);
@@ -483,7 +483,7 @@ _cairo_scaled_font_init (cairo_scaled_font_t               *scaled_font,
     if (scaled_font->glyphs == NULL)
 	return CAIRO_STATUS_NO_MEMORY;
 
-    scaled_font->ref_count = 1;
+    CAIRO_REFERENCE_COUNT_INIT (&scaled_font->ref_count, 1);
 
     _cairo_user_data_array_init (&scaled_font->user_data);
 
@@ -613,7 +613,7 @@ cairo_scaled_font_create (cairo_font_face_t          *font_face,
 	 * been found in font_map->holdovers, (which means this caching is
 	 * actually working). So now we remove it from the holdovers
 	 * array. */
-	if (scaled_font->ref_count == 0) {
+	if (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count)) {
 	    int i;
 
 	    for (i = 0; i < font_map->num_holdovers; i++)
@@ -635,7 +635,7 @@ cairo_scaled_font_create (cairo_font_face_t          *font_face,
 	     * than calling into cairo_scaled_font_reference), since we
 	     * must modify the reference count while our lock is still
 	     * held. */
-	    scaled_font->ref_count++;
+	    _cairo_reference_count_inc (&scaled_font->ref_count);
 	    _cairo_scaled_font_map_unlock ();
 	    return scaled_font;
 	}
@@ -686,16 +686,13 @@ slim_hidden_def (cairo_scaled_font_create);
 cairo_scaled_font_t *
 cairo_scaled_font_reference (cairo_scaled_font_t *scaled_font)
 {
-    if (scaled_font == NULL || scaled_font->ref_count == CAIRO_REF_COUNT_INVALID)
+    if (scaled_font == NULL ||
+	    CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count))
 	return scaled_font;
 
-    _cairo_scaled_font_map_lock ();
-    {
-	assert (scaled_font->ref_count > 0);
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count));
 
-	scaled_font->ref_count++;
-    }
-    _cairo_scaled_font_map_unlock ();
+    _cairo_reference_count_inc (&scaled_font->ref_count);
 
     return scaled_font;
 }
@@ -715,38 +712,37 @@ cairo_scaled_font_destroy (cairo_scaled_font_t *scaled_font)
     cairo_scaled_font_map_t *font_map;
     cairo_scaled_font_t *lru = NULL;
 
-    if (scaled_font == NULL || scaled_font->ref_count == CAIRO_REF_COUNT_INVALID)
+    if (scaled_font == NULL ||
+	    CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count))
 	return;
 
     font_map = _cairo_scaled_font_map_lock ();
-    {
-	assert (font_map != NULL);
+    assert (font_map != NULL);
 
-	assert (scaled_font->ref_count > 0);
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&scaled_font->ref_count));
 
-	if (--(scaled_font->ref_count) == 0)
+    if (_cairo_reference_count_dec_and_test (&scaled_font->ref_count)) {
+	/* Rather than immediately destroying this object, we put it into
+	 * the font_map->holdovers array in case it will get used again
+	 * soon (and is why we must hold the lock over the atomic op on
+	 * the reference count). To make room for it, we do actually
+	 * destroy the least-recently-used holdover.
+	 */
+	if (font_map->num_holdovers == CAIRO_SCALED_FONT_MAX_HOLDOVERS)
 	{
-	    /* Rather than immediately destroying this object, we put it into
-	     * the font_map->holdovers array in case it will get used again
-	     * soon. To make room for it, we do actually destroy the
-	     * least-recently-used holdover.
-	     */
-	    if (font_map->num_holdovers == CAIRO_SCALED_FONT_MAX_HOLDOVERS)
-	    {
-		lru = font_map->holdovers[0];
-		assert (lru->ref_count == 0);
-
-		_cairo_hash_table_remove (font_map->hash_table, &lru->hash_entry);
-
-		font_map->num_holdovers--;
-		memmove (&font_map->holdovers[0],
-			 &font_map->holdovers[1],
-			 font_map->num_holdovers * sizeof (cairo_scaled_font_t*));
-	    }
+	    lru = font_map->holdovers[0];
+	    assert (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&lru->ref_count));
+
+	    _cairo_hash_table_remove (font_map->hash_table, &lru->hash_entry);
 
-	    font_map->holdovers[font_map->num_holdovers] = scaled_font;
-	    font_map->num_holdovers++;
+	    font_map->num_holdovers--;
+	    memmove (&font_map->holdovers[0],
+		     &font_map->holdovers[1],
+		     font_map->num_holdovers * sizeof (cairo_scaled_font_t*));
 	}
+
+	font_map->holdovers[font_map->num_holdovers] = scaled_font;
+	font_map->num_holdovers++;
     }
     _cairo_scaled_font_map_unlock ();
 
@@ -777,10 +773,11 @@ slim_hidden_def (cairo_scaled_font_destroy);
 unsigned int
 cairo_scaled_font_get_reference_count (cairo_scaled_font_t *scaled_font)
 {
-    if (scaled_font == NULL || scaled_font->ref_count == CAIRO_REF_COUNT_INVALID)
+    if (scaled_font == NULL ||
+	    CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count))
 	return 0;
 
-    return scaled_font->ref_count;
+    return CAIRO_REFERENCE_COUNT_GET_VALUE (&scaled_font->ref_count);
 }
 
 /**
@@ -829,7 +826,7 @@ cairo_scaled_font_set_user_data (cairo_scaled_font_t	     *scaled_font,
 				 void			     *user_data,
 				 cairo_destroy_func_t	      destroy)
 {
-    if (scaled_font->ref_count == CAIRO_REF_COUNT_INVALID)
+    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&scaled_font->ref_count))
 	return CAIRO_STATUS_NO_MEMORY;
 
     return _cairo_user_data_array_set_data (&scaled_font->user_data,
diff --git a/src/cairo-surface-private.h b/src/cairo-surface-private.h
index a0b50c5..efd4365 100644
--- a/src/cairo-surface-private.h
+++ b/src/cairo-surface-private.h
@@ -41,6 +41,7 @@
 #include "cairo.h"
 
 #include "cairo-types-private.h"
+#include "cairo-reference-count-private.h"
 
 struct _cairo_surface {
     const cairo_surface_backend_t *backend;
@@ -52,7 +53,7 @@ struct _cairo_surface {
 
     cairo_content_t content;
 
-    unsigned int ref_count;
+    cairo_reference_count_t ref_count;
     cairo_status_t status;
     cairo_bool_t finished;
     cairo_user_data_array_t user_data;
diff --git a/src/cairo-surface.c b/src/cairo-surface.c
index 612d8cf..05dc906 100644
--- a/src/cairo-surface.c
+++ b/src/cairo-surface.c
@@ -46,7 +46,7 @@ const cairo_surface_t name = {					\
     &cairo_image_surface_backend,	/* backend */		\
     CAIRO_SURFACE_TYPE_IMAGE,					\
     CAIRO_CONTENT_COLOR,					\
-    CAIRO_REF_COUNT_INVALID,		/* ref_count */		\
+    CAIRO_REFERENCE_COUNT_INVALID,	/* ref_count */		\
     status,				/* status */		\
     FALSE,				/* finished */		\
     { 0,	/* size */					\
@@ -188,7 +188,7 @@ _cairo_surface_init (cairo_surface_t			*surface,
     surface->content = content;
     surface->type = backend->type;
 
-    surface->ref_count = 1;
+    CAIRO_REFERENCE_COUNT_INIT (&surface->ref_count, 1);
     surface->status = CAIRO_STATUS_SUCCESS;
     surface->finished = FALSE;
 
@@ -372,12 +372,13 @@ _cairo_surface_get_clip_mode (cairo_surface_t *surface)
 cairo_surface_t *
 cairo_surface_reference (cairo_surface_t *surface)
 {
-    if (surface == NULL || surface->ref_count == CAIRO_REF_COUNT_INVALID)
+    if (surface == NULL ||
+	    CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
 	return surface;
 
-    assert (surface->ref_count > 0);
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count));
 
-    surface->ref_count++;
+    _cairo_reference_count_inc (&surface->ref_count);
 
     return surface;
 }
@@ -394,13 +395,13 @@ slim_hidden_def (cairo_surface_reference);
 void
 cairo_surface_destroy (cairo_surface_t *surface)
 {
-    if (surface == NULL || surface->ref_count == CAIRO_REF_COUNT_INVALID)
+    if (surface == NULL ||
+	    CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
 	return;
 
-    assert (surface->ref_count > 0);
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&surface->ref_count));
 
-    surface->ref_count--;
-    if (surface->ref_count)
+    if (! _cairo_reference_count_dec_and_test (&surface->ref_count))
 	return;
 
     if (! surface->finished)
@@ -422,10 +423,11 @@ slim_hidden_def(cairo_surface_destroy);
 cairo_status_t
 _cairo_surface_reset (cairo_surface_t *surface)
 {
-    if (surface == NULL || surface->ref_count == CAIRO_REF_COUNT_INVALID)
+    if (surface == NULL ||
+	    CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
 	return CAIRO_STATUS_SUCCESS;
 
-    assert (surface->ref_count == 1);
+    assert (CAIRO_REFERENCE_COUNT_GET_VALUE (&surface->ref_count) == 1);
 
     _cairo_user_data_array_fini (&surface->user_data);
 
@@ -454,10 +456,11 @@ _cairo_surface_reset (cairo_surface_t *surface)
 unsigned int
 cairo_surface_get_reference_count (cairo_surface_t *surface)
 {
-    if (surface == NULL || surface->ref_count == CAIRO_REF_COUNT_INVALID)
+    if (surface == NULL ||
+	    CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
 	return 0;
 
-    return surface->ref_count;
+    return CAIRO_REFERENCE_COUNT_GET_VALUE (&surface->ref_count);
 }
 
 /**
@@ -486,7 +489,7 @@ cairo_surface_finish (cairo_surface_t *surface)
     if (surface == NULL)
 	return;
 
-    if (surface->ref_count == CAIRO_REF_COUNT_INVALID)
+    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
 	return;
 
     if (surface->finished) {
@@ -557,7 +560,7 @@ cairo_surface_set_user_data (cairo_surface_t		 *surface,
 			     void			 *user_data,
 			     cairo_destroy_func_t	 destroy)
 {
-    if (surface->ref_count == CAIRO_REF_COUNT_INVALID)
+    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&surface->ref_count))
 	return CAIRO_STATUS_NO_MEMORY;
 
     return _cairo_user_data_array_set_data (&surface->user_data,
diff --git a/src/cairo-xlib-display.c b/src/cairo-xlib-display.c
index 5d4f339..673c546 100644
--- a/src/cairo-xlib-display.c
+++ b/src/cairo-xlib-display.c
@@ -127,13 +127,9 @@ _cairo_xlib_display_reference (cairo_xlib_display_t *display)
     if (display == NULL)
 	return NULL;
 
-    /* use our mutex until we get a real atomic inc */
-    CAIRO_MUTEX_LOCK (display->mutex);
-
-    assert (display->ref_count > 0);
-    display->ref_count++;
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&display->ref_count));
 
-    CAIRO_MUTEX_UNLOCK (display->mutex);
+    _cairo_reference_count_inc (&display->ref_count);
 
     return display;
 }
@@ -144,27 +140,25 @@ _cairo_xlib_display_destroy (cairo_xlib_display_t *display)
     if (display == NULL)
 	return;
 
-    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;
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&display->ref_count));
 
-	    if (job->type == WORK && job->func.work.destroy != NULL)
-		job->func.work.destroy (job->func.work.data);
+    if (! _cairo_reference_count_dec_and_test (&display->ref_count))
+	return;
 
-	    _cairo_freelist_free (&display->wq_freelist, job);
-	}
-	_cairo_freelist_fini (&display->wq_freelist);
-	_cairo_freelist_fini (&display->hook_freelist);
+    /* destroy all outstanding notifies */
+    while (display->workqueue != NULL) {
+	cairo_xlib_job_t *job = display->workqueue;
+	display->workqueue = job->next;
 
-	CAIRO_MUTEX_UNLOCK (display->mutex);
+	if (job->type == WORK && job->func.work.destroy != NULL)
+	    job->func.work.destroy (job->func.work.data);
 
-	free (display);
-    } else
-	CAIRO_MUTEX_UNLOCK (display->mutex);
+	_cairo_freelist_free (&display->wq_freelist, job);
+    }
+    _cairo_freelist_fini (&display->wq_freelist);
+    _cairo_freelist_fini (&display->hook_freelist);
+
+    free (display);
 }
 
 static int
@@ -280,7 +274,7 @@ _cairo_xlib_display_get (Display *dpy)
     _cairo_freelist_init (&display->wq_freelist, sizeof (cairo_xlib_job_t));
     _cairo_freelist_init (&display->hook_freelist, sizeof (cairo_xlib_hook_t));
 
-    display->ref_count = 2; /* add one for the CloseDisplay */
+    CAIRO_REFERENCE_COUNT_INIT (&display->ref_count, 2); /* add one for the CloseDisplay */
     CAIRO_MUTEX_INIT (display->mutex);
     display->display = dpy;
     display->screens = NULL;
diff --git a/src/cairo-xlib-private.h b/src/cairo-xlib-private.h
index 28ab1d3..ce2e5ae 100644
--- a/src/cairo-xlib-private.h
+++ b/src/cairo-xlib-private.h
@@ -39,6 +39,7 @@
 
 #include "cairo-compiler-private.h"
 #include "cairo-freelist-private.h"
+#include "cairo-reference-count-private.h"
 #include "cairo-xlib-xrender-private.h"
 
 #include <X11/Xutil.h> /* for XDestroyImage */
@@ -58,7 +59,7 @@ struct _cairo_xlib_hook {
 
 struct _cairo_xlib_display {
     cairo_xlib_display_t *next;
-    unsigned int ref_count;
+    cairo_reference_count_t ref_count;
     cairo_mutex_t mutex;
 
     Display *display;
@@ -74,7 +75,7 @@ struct _cairo_xlib_display {
 
 struct _cairo_xlib_screen_info {
     cairo_xlib_screen_info_t *next;
-    unsigned int ref_count;
+    cairo_reference_count_t ref_count;
 
     cairo_xlib_display_t *display;
     Screen *screen;
diff --git a/src/cairo-xlib-screen.c b/src/cairo-xlib-screen.c
index 88f0899..578a33a 100644
--- a/src/cairo-xlib-screen.c
+++ b/src/cairo-xlib-screen.c
@@ -246,8 +246,9 @@ _cairo_xlib_screen_info_reference (cairo_xlib_screen_info_t *info)
     if (info == NULL)
 	return NULL;
 
-    assert (info->ref_count > 0);
-    info->ref_count++;
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&info->ref_count));
+
+    _cairo_reference_count_inc (&info->ref_count);
 
     return info;
 }
@@ -274,8 +275,9 @@ _cairo_xlib_screen_info_destroy (cairo_xlib_screen_info_t *info)
     if (info == NULL)
 	return;
 
-    assert (info->ref_count > 0);
-    if (--info->ref_count)
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&info->ref_count));
+
+    if (! _cairo_reference_count_dec_and_test (&info->ref_count))
 	return;
 
     CAIRO_MUTEX_LOCK (info->display->mutex);
@@ -330,7 +332,7 @@ _cairo_xlib_screen_info_get (Display *dpy, Screen *screen)
     } else {
 	info = malloc (sizeof (cairo_xlib_screen_info_t));
 	if (info != NULL) {
-	    info->ref_count = 2; /* Add one for display cache */
+	    CAIRO_REFERENCE_COUNT_INIT (&info->ref_count, 2); /* Add one for display cache */
 	    info->display = _cairo_xlib_display_reference (display);
 	    info->screen = screen;
 	    info->has_render = FALSE;
diff --git a/src/cairo.c b/src/cairo.c
index ff052b5..8652b28 100644
--- a/src/cairo.c
+++ b/src/cairo.c
@@ -45,7 +45,7 @@
 #define CAIRO_TOLERANCE_MINIMUM	0.0002 /* We're limited by 16 bits of sub-pixel precision */
 
 static const cairo_t _cairo_nil = {
-  CAIRO_REF_COUNT_INVALID,	/* ref_count */
+  CAIRO_REFERENCE_COUNT_INVALID,	/* ref_count */
   CAIRO_STATUS_NO_MEMORY,	/* status */
   { 0, 0, 0, NULL },		/* user_data */
   NULL,				/* gstate */
@@ -199,7 +199,7 @@ cairo_create (cairo_surface_t *target)
     if (cr == NULL)
 	return (cairo_t *) &_cairo_nil;
 
-    cr->ref_count = 1;
+    CAIRO_REFERENCE_COUNT_INIT (&cr->ref_count, 1);
 
     cr->status = CAIRO_STATUS_SUCCESS;
 
@@ -232,12 +232,12 @@ slim_hidden_def (cairo_create);
 cairo_t *
 cairo_reference (cairo_t *cr)
 {
-    if (cr == NULL || cr->ref_count == CAIRO_REF_COUNT_INVALID)
+    if (cr == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&cr->ref_count))
 	return cr;
 
-    assert (cr->ref_count > 0);
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&cr->ref_count));
 
-    cr->ref_count++;
+    _cairo_reference_count_inc (&cr->ref_count);
 
     return cr;
 }
@@ -253,13 +253,12 @@ cairo_reference (cairo_t *cr)
 void
 cairo_destroy (cairo_t *cr)
 {
-    if (cr == NULL || cr->ref_count == CAIRO_REF_COUNT_INVALID)
+    if (cr == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&cr->ref_count))
 	return;
 
-    assert (cr->ref_count > 0);
+    assert (CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&cr->ref_count));
 
-    cr->ref_count--;
-    if (cr->ref_count)
+    if (! _cairo_reference_count_dec_and_test (&cr->ref_count))
 	return;
 
     while (cr->gstate != cr->gstate_tail) {
@@ -323,7 +322,7 @@ cairo_set_user_data (cairo_t			 *cr,
 		     void			 *user_data,
 		     cairo_destroy_func_t	 destroy)
 {
-    if (cr->ref_count == CAIRO_REF_COUNT_INVALID)
+    if (CAIRO_REFERENCE_COUNT_IS_INVALID (&cr->ref_count))
 	return CAIRO_STATUS_NO_MEMORY;
 
     return _cairo_user_data_array_set_data (&cr->user_data,
@@ -344,10 +343,10 @@ cairo_set_user_data (cairo_t			 *cr,
 unsigned int
 cairo_get_reference_count (cairo_t *cr)
 {
-    if (cr == NULL || cr->ref_count == CAIRO_REF_COUNT_INVALID)
+    if (cr == NULL || CAIRO_REFERENCE_COUNT_IS_INVALID (&cr->ref_count))
 	return 0;
 
-    return cr->ref_count;
+    return CAIRO_REFERENCE_COUNT_GET_VALUE (&cr->ref_count);
 }
 
 /**
diff --git a/src/cairoint.h b/src/cairoint.h
index 695cb8f..be7079c 100644
--- a/src/cairoint.h
+++ b/src/cairoint.h
@@ -110,8 +110,6 @@ do {					\
     assert (NOT_REACHED);		\
 } while (0)
 
-#define CAIRO_REF_COUNT_INVALID ((unsigned int) -1)
-
 #define CAIRO_ALPHA_IS_OPAQUE(alpha) ((alpha) >= ((double)0xff00 / (double)0xffff))
 #define CAIRO_ALPHA_SHORT_IS_OPAQUE(alpha) ((alpha) >= 0xff00)
 #define CAIRO_ALPHA_IS_ZERO(alpha) ((alpha) <= 0.0)
@@ -168,6 +166,7 @@ be32_to_cpu(uint32_t v)
 #include "cairo-types-private.h"
 #include "cairo-cache-private.h"
 #include "cairo-fixed-private.h"
+#include "cairo-reference-count-private.h"
 
 typedef struct _cairo_region cairo_region_t;
 
@@ -384,9 +383,9 @@ typedef struct _cairo_unscaled_font_backend cairo_unscaled_font_backend_t;
  * glyph cache.
  */
 typedef struct _cairo_unscaled_font {
-    cairo_hash_entry_t hash_entry;
-    unsigned int ref_count;
-    const cairo_unscaled_font_backend_t *backend;
+    cairo_hash_entry_t			 hash_entry;
+    cairo_reference_count_t		 ref_count;
+    const cairo_unscaled_font_backend_t	*backend;
 } cairo_unscaled_font_t;
 
 typedef struct _cairo_scaled_glyph {
@@ -410,7 +409,7 @@ struct _cairo_font_face {
     /* hash_entry must be first */
     cairo_hash_entry_t hash_entry;
     cairo_status_t status;
-    unsigned int ref_count;
+    cairo_reference_count_t ref_count;
     cairo_user_data_array_t user_data;
     const cairo_font_face_backend_t *backend;
 };
@@ -883,14 +882,14 @@ typedef enum {
 #define CAIRO_FILTER_DEFAULT CAIRO_FILTER_BEST
 
 struct _cairo_pattern {
-    cairo_pattern_type_t    type;
-    unsigned int	    ref_count;
-    cairo_status_t          status;
-    cairo_user_data_array_t user_data;
-
-    cairo_matrix_t	    matrix;
-    cairo_filter_t	    filter;
-    cairo_extend_t	    extend;
+    cairo_pattern_type_t	type;
+    cairo_reference_count_t	ref_count;
+    cairo_status_t		status;
+    cairo_user_data_array_t	user_data;
+
+    cairo_matrix_t		matrix;
+    cairo_filter_t		filter;
+    cairo_extend_t		extend;
 };
 
 typedef struct _cairo_solid_pattern {
-- 
1.4.4.2


--df+09Je9rNq3P+GE--


More information about the cairo mailing list