Bug 1161170 - backport upstream fix for race in Cairo freed_pool. r=jrmuizel
authorLee Salzman <lsalzman@mozilla.com>
Thu, 21 Apr 2016 11:49:02 -0400
changeset 332217 9dd83314d24f5d156030a120443bb14dffb94d11
parent 332216 b251f7795f185b55a35855caa0a21416a7dfe0d4
child 332218 bcda23d0f3caf10c6a5433c632ae8314a692d0fb
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-beta@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs1161170
milestone48.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1161170 - backport upstream fix for race in Cairo freed_pool. r=jrmuizel
gfx/cairo/cairo/src/cairo-atomic-private.h
gfx/cairo/cairo/src/cairo-atomic.c
gfx/cairo/cairo/src/cairo-freed-pool-private.h
gfx/cairo/cairo/src/cairo-freed-pool.c
--- a/gfx/cairo/cairo/src/cairo-atomic-private.h
+++ b/gfx/cairo/cairo/src/cairo-atomic-private.h
@@ -74,16 +74,28 @@ CAIRO_BEGIN_DECLS
 typedef int cairo_atomic_int_t;
 
 static cairo_always_inline cairo_atomic_int_t
 _cairo_atomic_int_get (cairo_atomic_int_t *x)
 {
     return __atomic_load_n(x, __ATOMIC_SEQ_CST);
 }
 
+static cairo_always_inline cairo_atomic_int_t
+_cairo_atomic_int_get_relaxed (cairo_atomic_int_t *x)
+{
+    return __atomic_load_n(x, __ATOMIC_RELAXED);
+}
+
+static cairo_always_inline void
+_cairo_atomic_int_set_relaxed (cairo_atomic_int_t *x, cairo_atomic_int_t val)
+{
+    __atomic_store_n(x, val, __ATOMIC_RELAXED);
+}
+
 static cairo_always_inline void *
 _cairo_atomic_ptr_get (void **x)
 {
     return __atomic_load_n(x, __ATOMIC_SEQ_CST);
 }
 
 # define _cairo_atomic_int_inc(x) ((void) __atomic_fetch_add(x, 1, __ATOMIC_SEQ_CST))
 # define _cairo_atomic_int_dec(x) ((void) __atomic_fetch_sub(x, 1, __ATOMIC_SEQ_CST))
@@ -149,16 +161,18 @@ static cairo_always_inline void *
 
 #if HAVE_WIN32_ATOMIC_PRIMITIVES
 
 #define HAS_ATOMIC_OPS 1
 
 typedef volatile long cairo_atomic_int_t;
 
 # define _cairo_atomic_int_get(x) ((int)*x)
+# define _cairo_atomic_int_get_relaxed(x) ((int)*(x))
+# define _cairo_atomic_int_set_relaxed(x, val) (*(x) = (val))
 # define _cairo_atomic_ptr_get(x) ((void*)*x)
 
 # define _cairo_atomic_int_inc(x) ((void) InterlockedIncrement(x))
 # define _cairo_atomic_int_dec(x) ((void) InterlockedDecrement(x))
 # define _cairo_atomic_int_dec_and_test(x) (InterlockedDecrement(x) == 0)
 # define _cairo_atomic_int_cmpxchg(x, oldv, newv) (InterlockedCompareExchange(x, newv, oldv) == oldv)
 # define _cairo_atomic_int_cmpxchg_return_old(x, oldv, newv) InterlockedCompareExchange(x, newv, oldv)
 
@@ -178,24 +192,38 @@ typedef int cairo_atomic_int_t;
 #ifdef ATOMIC_OP_NEEDS_MEMORY_BARRIER
 static cairo_always_inline cairo_atomic_int_t
 _cairo_atomic_int_get (cairo_atomic_int_t *x)
 {
     __sync_synchronize ();
     return *x;
 }
 
+static cairo_always_inline cairo_atomic_int_t
+_cairo_atomic_int_get_relaxed (cairo_atomic_int_t *x)
+{
+    return *x;
+}
+
+static cairo_always_inline void
+_cairo_atomic_int_set_relaxed (cairo_atomic_int_t *x, cairo_atomic_int_t val)
+{
+    *x = val;
+}
+
 static cairo_always_inline void *
 _cairo_atomic_ptr_get (void **x)
 {
     __sync_synchronize ();
     return *x;
 }
 #else
 # define _cairo_atomic_int_get(x) (*x)
+# define _cairo_atomic_int_get_relaxed(x) (*(x))
+# define _cairo_atomic_int_set_relaxed(x, val) (*(x) = (val))
 # define _cairo_atomic_ptr_get(x) (*x)
 #endif
 
 # define _cairo_atomic_int_inc(x) ((void) __sync_fetch_and_add(x, 1))
 # define _cairo_atomic_int_dec_and_test(x) (__sync_fetch_and_add(x, -1) == 1)
 # define _cairo_atomic_int_cmpxchg(x, oldv, newv) __sync_bool_compare_and_swap (x, oldv, newv)
 # define _cairo_atomic_int_cmpxchg_return_old(x, oldv, newv) __sync_val_compare_and_swap (x, oldv, newv)
 
@@ -220,16 +248,18 @@ typedef long long cairo_atomic_intptr_t;
 #if HAVE_LIB_ATOMIC_OPS
 #include <atomic_ops.h>
 
 #define HAS_ATOMIC_OPS 1
 
 typedef  AO_t cairo_atomic_int_t;
 
 # define _cairo_atomic_int_get(x) (AO_load_full (x))
+# define _cairo_atomic_int_get_relaxed(x) (AO_load_full (x))
+# define _cairo_atomic_int_set_relaxed(x, val) (AO_store_full ((x), (val)))
 
 # define _cairo_atomic_int_inc(x) ((void) AO_fetch_and_add1_full(x))
 # define _cairo_atomic_int_dec_and_test(x) (AO_fetch_and_sub1_full(x) == 1)
 # define _cairo_atomic_int_cmpxchg(x, oldv, newv) AO_compare_and_swap_full(x, oldv, newv)
 
 #if SIZEOF_VOID_P==SIZEOF_INT
 typedef unsigned int cairo_atomic_intptr_t;
 #elif SIZEOF_VOID_P==SIZEOF_LONG
@@ -249,16 +279,18 @@ typedef unsigned long long cairo_atomic_
 #if HAVE_OS_ATOMIC_OPS
 #include <libkern/OSAtomic.h>
 
 #define HAS_ATOMIC_OPS 1
 
 typedef int32_t cairo_atomic_int_t;
 
 # define _cairo_atomic_int_get(x) (OSMemoryBarrier(), *(x))
+# define _cairo_atomic_int_get_relaxed(x) (*(x))
+# define _cairo_atomic_int_set_relaxed(x, val) (*(x) = (val))
 
 # define _cairo_atomic_int_inc(x) ((void) OSAtomicIncrement32Barrier (x))
 # define _cairo_atomic_int_dec_and_test(x) (OSAtomicDecrement32Barrier (x) == 0)
 # define _cairo_atomic_int_cmpxchg(x, oldv, newv) OSAtomicCompareAndSwap32Barrier(oldv, newv, x)
 
 #if SIZEOF_VOID_P==4
 typedef int32_t cairo_atomic_intptr_t;
 # define _cairo_atomic_ptr_cmpxchg(x, oldv, newv) \
@@ -304,19 +336,25 @@ cairo_private void *
 _cairo_atomic_ptr_cmpxchg_return_old_impl (void **x, void *oldv, void *newv);
 
 #define _cairo_atomic_int_cmpxchg_return_old(x, oldv, newv) _cairo_atomic_int_cmpxchg_return_old_impl (x, oldv, newv)
 #define _cairo_atomic_ptr_cmpxchg_return_old(x, oldv, newv) _cairo_atomic_ptr_cmpxchg_return_old_impl (x, oldv, newv)
 
 #ifdef ATOMIC_OP_NEEDS_MEMORY_BARRIER
 cairo_private cairo_atomic_int_t
 _cairo_atomic_int_get (cairo_atomic_int_t *x);
+cairo_private cairo_atomic_int_t
+_cairo_atomic_int_get_relaxed (cairo_atomic_int_t *x);
+void
+_cairo_atomic_int_set_relaxed (cairo_atomic_int_t *x, cairo_atomic_int_t val);
 # define _cairo_atomic_ptr_get(x) (void *) _cairo_atomic_int_get((cairo_atomic_int_t *) x)
 #else
 # define _cairo_atomic_int_get(x) (*x)
+# define _cairo_atomic_int_get_relaxed(x) (*(x))
+# define _cairo_atomic_int_set_relaxed(x, val) (*(x) = (val))
 # define _cairo_atomic_ptr_get(x) (*x)
 #endif
 
 #else
 
 /* Workaround GCC complaining about casts */
 static cairo_always_inline void *
 _cairo_atomic_intptr_to_voidptr (cairo_atomic_intptr_t x)
--- a/gfx/cairo/cairo/src/cairo-atomic.c
+++ b/gfx/cairo/cairo/src/cairo-atomic.c
@@ -96,11 +96,25 @@ cairo_atomic_intptr_t
     cairo_atomic_intptr_t ret;
 
     CAIRO_MUTEX_LOCK (_cairo_atomic_mutex);
     ret = *x;
     CAIRO_MUTEX_UNLOCK (_cairo_atomic_mutex);
 
     return ret;
 }
+
+cairo_atomic_intptr_t
+_cairo_atomic_int_get_relaxed (cairo_atomic_intptr_t *x)
+{
+    return _cairo_atomic_int_get (x);
+}
+
+void
+_cairo_atomic_int_set_relaxed (cairo_atomic_intptr_t *x, cairo_atomic_intptr_t val)
+{
+    CAIRO_MUTEX_LOCK (_cairo_atomic_mutex);
+    *x = val;
+    CAIRO_MUTEX_UNLOCK (_cairo_atomic_mutex);
+}
 #endif
 
 #endif
--- a/gfx/cairo/cairo/src/cairo-freed-pool-private.h
+++ b/gfx/cairo/cairo/src/cairo-freed-pool-private.h
@@ -42,17 +42,17 @@
 
 #if HAS_ATOMIC_OPS
 /* Keep a stash of recently freed clip_paths, since we need to
  * reallocate them frequently.
  */
 #define MAX_FREED_POOL_SIZE 4
 typedef struct {
     void *pool[MAX_FREED_POOL_SIZE];
-    int top;
+    cairo_atomic_int_t top;
 } freed_pool_t;
 
 static cairo_always_inline void *
 _atomic_fetch (void **slot)
 {
     void *ptr;
 
     do {
@@ -72,43 +72,43 @@ cairo_private void *
 _freed_pool_get_search (freed_pool_t *pool);
 
 static inline void *
 _freed_pool_get (freed_pool_t *pool)
 {
     void *ptr;
     int i;
 
-    i = pool->top - 1;
+    i = _cairo_atomic_int_get_relaxed (&pool->top) - 1;
     if (i < 0)
 	i = 0;
 
     ptr = _atomic_fetch (&pool->pool[i]);
     if (likely (ptr != NULL)) {
-	pool->top = i;
+	_cairo_atomic_int_set_relaxed (&pool->top, i);
 	return ptr;
     }
 
     /* either empty or contended */
     return _freed_pool_get_search (pool);
 }
 
 cairo_private void
 _freed_pool_put_search (freed_pool_t *pool, void *ptr);
 
 static inline void
 _freed_pool_put (freed_pool_t *pool, void *ptr)
 {
     int i;
 
-    i = pool->top;
+    i = _cairo_atomic_int_get_relaxed (&pool->top);
     if (likely (i < ARRAY_LENGTH (pool->pool) &&
 		_atomic_store (&pool->pool[i], ptr)))
     {
-	pool->top = i + 1;
+	_cairo_atomic_int_set_relaxed (&pool->top, i + 1);
 	return;
     }
 
     /* either full or contended */
     _freed_pool_put_search (pool, ptr);
 }
 
 cairo_private void
--- a/gfx/cairo/cairo/src/cairo-freed-pool.c
+++ b/gfx/cairo/cairo/src/cairo-freed-pool.c
@@ -45,49 +45,49 @@ void *
 _freed_pool_get_search (freed_pool_t *pool)
 {
     void *ptr;
     int i;
 
     for (i = ARRAY_LENGTH (pool->pool); i--;) {
 	ptr = _atomic_fetch (&pool->pool[i]);
 	if (ptr != NULL) {
-	    pool->top = i;
+	    _cairo_atomic_int_set_relaxed (&pool->top, i);
 	    return ptr;
 	}
     }
 
     /* empty */
-    pool->top = 0;
+    _cairo_atomic_int_set_relaxed (&pool->top, 0);
     return NULL;
 }
 
 void
 _freed_pool_put_search (freed_pool_t *pool, void *ptr)
 {
     int i;
 
     for (i = 0; i < ARRAY_LENGTH (pool->pool); i++) {
 	if (_atomic_store (&pool->pool[i], ptr)) {
-	    pool->top = i + 1;
+	    _cairo_atomic_int_set_relaxed (&pool->top, i + 1);
 	    return;
 	}
     }
 
     /* full */
-    pool->top = i;
+    _cairo_atomic_int_set_relaxed (&pool->top, i);
     free (ptr);
 }
 
 void
 _freed_pool_reset (freed_pool_t *pool)
 {
     int i;
 
     for (i = 0; i < ARRAY_LENGTH (pool->pool); i++) {
 	free (pool->pool[i]);
 	pool->pool[i] = NULL;
     }
 
-    pool->top = 0;
+    _cairo_atomic_int_set_relaxed (&pool->top, 0);
 }
 
 #endif