diff options
author | Mikhail Fludkov <misha@pexip.com> | 2017-10-06 13:47:51 +0200 |
---|---|---|
committer | Adrian Johnson <ajohnson@redneon.com> | 2017-10-15 18:51:04 +1030 |
commit | 90104809b0e03d28ac1152034fd4f05fc8e97b9a (patch) | |
tree | 3b2cd2961a1d85c56e8edaaf3c3cdb9f435ac7d0 /src/cairo-atomic-private.h | |
parent | 79e0e25e441a74e3ec207d95bd83437457ba1885 (diff) | |
download | cairo-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.h | 33 |
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 |