diff --git a/meson.build b/meson.build index 48fd6bb13..08ce956fe 100644 --- a/meson.build +++ b/meson.build @@ -766,22 +766,28 @@ endforeach extra_link_args += pthread_link_args -# Atomics are an optional feature in C11. Also need to check that C11 atomics are lock free. -# On Windows we use the Interlocked family of functions -if host_machine.system() != 'windows' - if cc.links(files('meson-cc-tests/atomic-ops-c11.c'), name: 'Atomic ops: c11') - conf.set('HAVE_C11_ATOMIC_PRIMITIVES', 1) - elif cc.links(files('meson-cc-tests/atomic-ops-cxx11.c'), name: 'Atomic ops: cxx11') - conf.set('HAVE_CXX11_ATOMIC_PRIMITIVES', 1) - elif cc.links(files('meson-cc-tests/atomic-ops-gcc-legacy.c'), name: 'Atomic ops: gcc legacy') - conf.set('HAVE_GCC_LEGACY_ATOMICS', 1) - elif cc.has_header('atomic_ops.h') - conf.set('HAVE_LIB_ATOMIC_OPS', 1) - elif cc.has_header('libkern/OSAtomic.h') - conf.set('HAVE_OS_ATOMIC_OPS', 1) - else - warning('Atomic ops not supported.') - endif +cpp_enabled = host_machine.system() == 'windows' + +if not cpp_enabled and cc.links(files('meson-cc-tests/atomic-ops-c11.c'), name: 'Atomic ops: c11') + # Currently we avoid C11 atomics when using both C and C++. The standards + # do not guarantee compatibility between C11 atomics and C++11 std::atomic + # (though effort is underway, see C++/N2741). We can enable this for selected + # compilers over time. + # + # When not using C++, check if C11 atomics are available and whether atomic + # ints and pointers are lock-free. + conf.set('HAVE_C11_ATOMIC_PRIMITIVES', 1) +elif cc.links(files('meson-cc-tests/atomic-ops-cxx11.c'), name: 'Atomic ops: cxx11') + conf.set('HAVE_CXX11_ATOMIC_PRIMITIVES', 1) +elif cc.links(files('meson-cc-tests/atomic-ops-gcc-legacy.c'), name: 'Atomic ops: gcc legacy') + conf.set('HAVE_GCC_LEGACY_ATOMICS', 1) +elif host_machine.system() != 'windows' and dependency('atomic_ops', required: false).found() + internal_deps += [dependency('atomic_ops')] + conf.set('HAVE_LIB_ATOMIC_OPS', 1) +elif host_machine.system() == 'darwin' and cc.has_header('libkern/OSAtomic.h') + conf.set('HAVE_OS_ATOMIC_OPS', 1) +elif not cc.has_define('_MSC_VER') + warning('Atomic ops not supported.') endif test_mkdir_c_args = [] diff --git a/src/cairo-atomic-private.h b/src/cairo-atomic-private.h index d8d805771..1f3ce1ffd 100644 --- a/src/cairo-atomic-private.h +++ b/src/cairo-atomic-private.h @@ -274,26 +274,35 @@ _cairo_atomic_ptr_get (cairo_atomic_intptr_t *x) #endif /* HAVE_GCC_LEGACY_ATOMICS */ #if HAVE_LIB_ATOMIC_OPS + #include +#include #define HAS_ATOMIC_OPS 1 -typedef AO_t cairo_atomic_int_t; +typedef int 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))) +/* Casts from signed to unsigned must not change representation */ +static_assert((unsigned)-1 == UINT_MAX, + "We require two's complement representation of signed integrals"); -# define _cairo_atomic_int_inc(x) ((void) AO_fetch_and_add1_full(x)) -# define _cairo_atomic_int_dec(x) ((void) AO_fetch_and_sub1_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) +# define _cairo_atomic_int_get(x) ((int)AO_int_load_full ((unsigned *)(x))) +# define _cairo_atomic_int_get_relaxed(x) ((int)AO_int_load ((unsigned *)(x))) +# define _cairo_atomic_int_set_relaxed(x, val) (AO_int_store ((unsigned *)(x), (unsigned)(val))) -typedef intptr_t cairo_atomic_intptr_t; +# define _cairo_atomic_int_inc(x) ((void) AO_int_fetch_and_add1_full((unsigned *)(x))) +# define _cairo_atomic_int_dec(x) ((void) AO_int_fetch_and_sub1_full((unsigned *)(x))) +# define _cairo_atomic_int_dec_and_test(x) (AO_int_fetch_and_sub1_full((unsigned *)(x)) == 1U) +# define _cairo_atomic_int_cmpxchg(x, oldv, newv) AO_int_compare_and_swap_full((unsigned *)x, (unsigned)(oldv), (unsigned)(newv)) +# define _cairo_atomic_int_cmpxchg_return_old(x, oldv, newv) AO_int_fetch_compare_and_swap_full((unsigned *)x, (unsigned)(oldv), (unsigned)(newv)) + +typedef AO_t cairo_atomic_intptr_t; + +static_assert (sizeof (AO_t) >= sizeof (void *), "AO_t cannot be used for pointers"); # define _cairo_atomic_ptr_get(x) _cairo_atomic_intptr_to_voidptr (AO_load_full (x)) -# define _cairo_atomic_ptr_cmpxchg(x, oldv, newv) \ - _cairo_atomic_int_cmpxchg ((cairo_atomic_intptr_t*)(x), (cairo_atomic_intptr_t)oldv, (cairo_atomic_intptr_t)newv) +# define _cairo_atomic_ptr_cmpxchg(x, oldv, newv) AO_compare_and_swap_full(x, (AO_t)oldv, (AO_t)newv) +# define _cairo_atomic_ptr_cmpxchg_return_old(x, oldv, newv) AO_fetch_compare_and_swap_full(x, (AO_t)oldv, (AO_t)newv) #endif @@ -330,7 +339,7 @@ typedef intptr_t cairo_atomic_intptr_t; #endif /* HAVE_OS_ATOMIC_OPS */ -#if !defined(HAS_ATOMIC_OPS) && defined(_WIN32) +#if !defined(HAS_ATOMIC_OPS) && defined(_MSC_VER) #include #define HAS_ATOMIC_OPS 1 @@ -353,13 +362,27 @@ _cairo_atomic_int_get (cairo_atomic_int_t *x) # define _cairo_atomic_int_dec_and_test(x) (InterlockedDecrement (x) == 0) static cairo_always_inline cairo_bool_t -_cairo_atomic_int_cmpxchg (cairo_atomic_int_t *x, - int oldv, - int newv) +_cairo_atomic_int_cmpxchg_impl (cairo_atomic_int_t *x, + int oldv, + int newv) { return InterlockedCompareExchange (x, (LONG)newv, (LONG)oldv) == oldv; } +#define _cairo_atomic_int_cmpxchg(x, oldv, newv) \ + _cairo_atomic_int_cmpxchg_impl(x, oldv, newv) + +static cairo_always_inline int +_cairo_atomic_int_cmpxchg_return_old_impl (cairo_atomic_int_t *x, + int oldv, + int newv) +{ + return (int) InterlockedCompareExchange (x, (LONG)newv, (LONG)oldv); +} + +#define _cairo_atomic_int_cmpxchg_return_old(x, oldv, newv) \ + _cairo_atomic_int_cmpxchg_return_old_impl(x, oldv, newv) + static cairo_always_inline void * _cairo_atomic_ptr_get (cairo_atomic_intptr_t *x) { @@ -368,17 +391,23 @@ _cairo_atomic_ptr_get (cairo_atomic_intptr_t *x) } static cairo_always_inline cairo_bool_t -_cairo_atomic_ptr_cmpxchg (cairo_atomic_intptr_t *x, void *oldv, void *newv) +_cairo_atomic_ptr_cmpxchg_impl (cairo_atomic_intptr_t *x, void *oldv, void *newv) { return InterlockedCompareExchangePointer (x, newv, oldv) == oldv; } +#define _cairo_atomic_ptr_cmpxchg(x, oldv, newv) \ + _cairo_atomic_ptr_cmpxchg_impl(x, oldv, newv) + static cairo_always_inline void * -_cairo_atomic_ptr_cmpxchg_return_old (cairo_atomic_intptr_t *x, void *oldv, void *newv) +_cairo_atomic_ptr_cmpxchg_return_old_impl (cairo_atomic_intptr_t *x, void *oldv, void *newv) { return InterlockedCompareExchangePointer (x, newv, oldv); } +#define _cairo_atomic_ptr_cmpxchg_return_old(x, oldv, newv) \ + _cairo_atomic_ptr_cmpxchg_return_old_impl(x, oldv, newv) + #endif /* !defined(HAS_ATOMIC_OPS) && defined(_WIN32) */