summaryrefslogtreecommitdiff
path: root/src/cairo-atomic-private.h
diff options
context:
space:
mode:
authorMikhail Fludkov <misha@pexip.com>2017-10-06 13:47:51 +0200
committerAdrian Johnson <ajohnson@redneon.com>2017-10-15 18:51:04 +1030
commit90104809b0e03d28ac1152034fd4f05fc8e97b9a (patch)
tree3b2cd2961a1d85c56e8edaaf3c3cdb9f435ac7d0 /src/cairo-atomic-private.h
parent79e0e25e441a74e3ec207d95bd83437457ba1885 (diff)
downloadcairo-90104809b0e03d28ac1152034fd4f05fc8e97b9a.tar.gz
Surround initialisations with atomic critical section
Fixes the race condition when one thread uses cairo_mask_compositor_t pointer returned by _cairo_image_mask_compositor_get, while another one started but has not finished it's initialisation yet Usage: static cairo_atomic_once_t once = CAIRO_ATOMIC_ONCE_INIT; if (_cairo_atomic_init_once_enter(&once)) { /* Initialization code */ _cairo_atomic_init_once_leave(&once); } https://bugs.freedesktop.org/show_bug.cgi?id=103037
Diffstat (limited to 'src/cairo-atomic-private.h')
-rw-r--r--src/cairo-atomic-private.h33
1 files changed, 33 insertions, 0 deletions
diff --git a/src/cairo-atomic-private.h b/src/cairo-atomic-private.h
index 500129039..723a326ce 100644
--- a/src/cairo-atomic-private.h
+++ b/src/cairo-atomic-private.h
@@ -45,6 +45,8 @@
#include "config.h"
#endif
+#include <assert.h>
+
/* The autoconf on OpenBSD 4.5 produces the malformed constant name
* SIZEOF_VOID__ rather than SIZEOF_VOID_P. Work around that here. */
#if !defined(SIZEOF_VOID_P) && defined(SIZEOF_VOID__)
@@ -393,6 +395,37 @@ _cairo_atomic_ptr_cmpxchg_return_old_fallback(void **x, void *oldv, void *newv)
(void) ret__; \
} while (0)
+typedef cairo_atomic_int_t cairo_atomic_once_t;
+
+#define CAIRO_ATOMIC_ONCE_UNINITIALIZED (0)
+#define CAIRO_ATOMIC_ONCE_INITIALIZING (1)
+#define CAIRO_ATOMIC_ONCE_INITIALIZED (2)
+#define CAIRO_ATOMIC_ONCE_INIT CAIRO_ATOMIC_ONCE_UNINITIALIZED
+
+static cairo_always_inline cairo_bool_t
+_cairo_atomic_init_once_enter(cairo_atomic_once_t *once)
+{
+ if (likely(_cairo_atomic_int_get(once) == CAIRO_ATOMIC_ONCE_INITIALIZED))
+ return 0;
+
+ if (_cairo_atomic_int_cmpxchg(once,
+ CAIRO_ATOMIC_ONCE_UNINITIALIZED,
+ CAIRO_ATOMIC_ONCE_INITIALIZING))
+ return 1;
+
+ while (_cairo_atomic_int_get(once) != CAIRO_ATOMIC_ONCE_INITIALIZED) {}
+ return 0;
+}
+
+static cairo_always_inline void
+_cairo_atomic_init_once_leave(cairo_atomic_once_t *once)
+{
+ if (unlikely(!_cairo_atomic_int_cmpxchg(once,
+ CAIRO_ATOMIC_ONCE_INITIALIZING,
+ CAIRO_ATOMIC_ONCE_INITIALIZED)))
+ assert (0 && "incorrect use of _cairo_atomic_init_once API (once != CAIRO_ATOMIC_ONCE_INITIALIZING)");
+}
+
CAIRO_END_DECLS
#endif