X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=include%2Fvlc_atomic.h;h=af88eabb5e7af12d38e78ae972db728d496aa68f;hb=f5235164c53588314b4a4f67109cd0035d5fb35b;hp=6a1f435c6da473089684ff4ec4ffeaee3821c1fa;hpb=cbbb1e4917d918aaa5354729ad94a78d568db627;p=vlc diff --git a/include/vlc_atomic.h b/include/vlc_atomic.h index 6a1f435c6d..af88eabb5e 100644 --- a/include/vlc_atomic.h +++ b/include/vlc_atomic.h @@ -28,9 +28,11 @@ # if !defined (__cplusplus) && (__STDC_VERSION__ >= 201112L) \ && !defined (__STDC_NO_ATOMICS__) + +/*** Native C11 atomics ***/ # include -# else /* if (???) */ +# else # define ATOMIC_FLAG_INIT false @@ -54,45 +56,62 @@ /* In principles, __sync_*() only supports int, long and long long and their * unsigned equivalents, i.e. 4-bytes and 8-bytes types, although GCC also * supports 1 and 2-bytes types. Some non-x86 architectures do not support - * 8-byte atomic types (or not efficiently). So lets stick to (u)intptr_t. */ -typedef intptr_t atomic_flag; -typedef intptr_t atomic_bool; -typedef intptr_t atomic_char; -typedef intptr_t atomic_schar; -typedef uintptr_t atomic_uchar; -typedef intptr_t atomic_short; -typedef uintptr_t atomic_ushort; -typedef intptr_t atomic_int; -typedef uintptr_t atomic_uint; -typedef intptr_t atomic_long; -typedef uintptr_t atomic_ulong; -//atomic_llong -//atomic_ullong -typedef uintptr_t atomic_char16_t; -typedef uintptr_t atomic_char32_t; -typedef uintptr_t atomic_wchar_t; -typedef intptr_t atomic_int_least8_t; -typedef uintptr_t atomic_uint_least8_t; -typedef intptr_t atomic_int_least16_t; -typedef uintptr_t atomic_uint_least16_t; -typedef intptr_t atomic_int_least32_t; -typedef uintptr_t atomic_uint_least32_t; -//atomic_int_least64_t -//atomic_uint_least64_t -typedef intptr_t atomic_int_fast8_t; -typedef uintptr_t atomic_uint_fast8_t; -typedef intptr_t atomic_int_fast16_t; -typedef uintptr_t atomic_uint_fast16_t; -typedef intptr_t atomic_int_fast32_t; -typedef uintptr_t atomic_uint_fast32_t; -//atomic_int_fast64_t -//atomic_uint_fast64_t -typedef intptr_t atomic_intptr_t; -typedef uintptr_t atomic_uintptr_t; -typedef uintptr_t atomic_size_t; -typedef intptr_t atomic_ptrdiff_t; -//atomic_intmax_t -//atomic_uintmax_t + * 8-byte atomic types (or not efficiently). */ +# if defined (_MSC_VER) +/* Some atomic operations of the Interlocked API are only + available for desktop apps. Thus we define the atomic types to + be at least 32 bits wide. */ +typedef int_least32_t atomic_flag; +typedef int_least32_t atomic_bool; +typedef int_least32_t atomic_char; +typedef int_least32_t atomic_schar; +typedef uint_least32_t atomic_uchar; +typedef int_least32_t atomic_short; +typedef uint_least32_t atomic_ushort; +# else +typedef bool atomic_flag; +typedef bool atomic_bool; +typedef char atomic_char; +typedef signed char atomic_schar; +typedef unsigned char atomic_uchar; +typedef short atomic_short; +typedef unsigned short atomic_ushort; +# endif +typedef int atomic_int; +typedef unsigned int atomic_uint; +typedef long atomic_long; +typedef unsigned long atomic_ulong; +typedef long long atomic_llong; +typedef unsigned long long atomic_ullong; +//typedef char16_t atomic_char16_t; +//typedef char32_t atomic_char32_t; +typedef wchar_t atomic_wchar_t; +typedef int_least8_t atomic_int_least8_t; +typedef uint_least8_t atomic_uint_least8_t; +typedef int_least16_t atomic_int_least16_t; +typedef uint_least16_t atomic_uint_least16_t; +typedef int_least32_t atomic_int_least32_t; +typedef uint_least32_t atomic_uint_least32_t; +typedef int_least64_t atomic_int_least64_t; +typedef uint_least64_t atomic_uint_least64_t; +typedef int_fast8_t atomic_int_fast8_t; +typedef uint_fast8_t atomic_uint_fast8_t; +typedef int_fast16_t atomic_int_fast16_t; +typedef uint_fast16_t atomic_uint_fast16_t; +typedef int_fast32_t atomic_int_fast32_t; +typedef uint_fast32_t atomic_uint_fast32_t; +typedef int_fast64_t atomic_int_fast64_t; +typedef uint_fast64_t atomic_uint_fast64_t; +typedef intptr_t atomic_intptr_t; +typedef uintptr_t atomic_uintptr_t; +typedef size_t atomic_size_t; +typedef ptrdiff_t atomic_ptrdiff_t; +typedef intmax_t atomic_intmax_t; +typedef uintmax_t atomic_uintmax_t; + +# if defined (__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4) || (defined (__clang__) && (defined (__x86_64__) || defined (__i386__))) + +/*** Intel/GCC atomics ***/ # define atomic_store(object,desired) \ do { \ @@ -109,52 +128,35 @@ typedef intptr_t atomic_ptrdiff_t; # define atomic_load_explicit(object,order) \ atomic_load(object) -static inline -intptr_t vlc_atomic_exchange(volatile void *object, intptr_t desired) -{ - volatile intptr_t *ptr = (volatile intptr_t *)object; - intptr_t old; - /* NOTE: while __sync_lock_test_and_set() is an atomic exchange, its memory - * order is too weak (acquire instead of sequentially consistent). - * Because of that, for lack of both C11 _Generic() and GNU C compound - * statements, atomic exchange needs a helper function. - * Thus all atomic types must have the same size. */ - do - old = atomic_load(ptr); - while (!__sync_bool_compare_and_swap(ptr, old, desired)); - - return old; -} - # define atomic_exchange(object,desired) \ - vlc_atomic_exchange(object,desired) +({ \ + typeof (object) _obj = (object); \ + typeof (*object) _old; \ + do \ + _old = atomic_load(_obj); \ + while (!__sync_bool_compare_and_swap(_obj, _old, (desired))); \ + _old; \ +}) # define atomic_exchange_explicit(object,desired,order) \ atomic_exchange(object,desired) -static inline -bool vlc_atomic_compare_exchange(volatile void *object, void *expected, - intptr_t desired) -{ - volatile intptr_t *ptr = (volatile intptr_t *)object; - intptr_t old = *(intptr_t *)expected; - intptr_t val = __sync_val_compare_and_swap(ptr, old, desired); - if (old != val) - { - *(intptr_t *)expected = val; - return false; - } - return true; -} +# define atomic_compare_exchange(object,expected,desired) \ +({ \ + typeof (object) _exp = (expected); \ + typeof (*object) _old = *_exp; \ + *_exp = __sync_val_compare_and_swap((object), _old, (desired)); \ + *_exp == _old; \ +}) # define atomic_compare_exchange_strong(object,expected,desired) \ - vlc_atomic_compare_exchange(object, expected, desired) + atomic_compare_exchange(object, expected, desired) # define atomic_compare_exchange_strong_explicit(object,expected,desired,order) \ atomic_compare_exchange_strong(object, expected, desired) # define atomic_compare_exchange_weak(object,expected,desired) \ - vlc_atomic_compare_exchange(object, expected, desired) + atomic_compare_exchange(object, expected, desired) # define atomic_compare_exchange_weak_explicit(object,expected,desired,order) \ atomic_compare_exchange_weak(object, expected, desired) @@ -201,69 +203,228 @@ bool vlc_atomic_compare_exchange(volatile void *object, void *expected, # define atomic_flag_clear_explicit(object,order) \ atomic_flag_clear(object) -# endif +# elif defined (__GNUC__) -/** Static initializer for \ref vlc_atomic_t */ -# define VLC_ATOMIC_INIT(val) { (val) } +/*** No atomics ***/ -/* All functions return the atom value _after_ the operation. */ -static inline uintptr_t vlc_atomic_get(const vlc_atomic_t *atom) -{ - return atomic_load(&atom->u); -} +# define atomic_store(object,desired) \ + do { \ + typeof (object) _obj = (object); \ + typeof (*object) _des = (desired); \ + vlc_global_lock(VLC_ATOMIC_MUTEX); \ + *_obj = _des; \ + vlc_global_unlock(VLC_ATOMIC_MUTEX); \ + } while (0) +# define atomic_store_explicit(object,desired,order) \ + atomic_store(object,desired) -static inline uintptr_t vlc_atomic_set(vlc_atomic_t *atom, uintptr_t v) -{ - atomic_store(&atom->u, v); - return v; -} +# define atomic_load(object) \ +({ \ + typeof (object) _obj = (object); \ + typeof (*object) _old; \ + vlc_global_lock(VLC_ATOMIC_MUTEX); \ + _old = *_obj; \ + vlc_global_unlock(VLC_ATOMIC_MUTEX); \ + _old; \ +}) +# define atomic_load_explicit(object,order) \ + atomic_load(object) -static inline uintptr_t vlc_atomic_add(vlc_atomic_t *atom, uintptr_t v) -{ - return atomic_fetch_add(&atom->u, v) + v; -} +# define atomic_exchange(object,desired) \ +({ \ + typeof (object) _obj = (object); \ + typeof (*object) _des = (desired); \ + typeof (*object) _old; \ + vlc_global_lock(VLC_ATOMIC_MUTEX); \ + _old = *_obj; \ + *_obj = _des; \ + vlc_global_unlock(VLC_ATOMIC_MUTEX); \ + _old; \ +}) +# define atomic_exchange_explicit(object,desired,order) \ + atomic_exchange(object,desired) -static inline uintptr_t vlc_atomic_sub (vlc_atomic_t *atom, uintptr_t v) -{ - return atomic_fetch_sub (&atom->u, v) - v; -} +# define atomic_compare_exchange_strong(object,expected,desired) \ +({ \ + typeof (object) _obj = (object); \ + typeof (object) _exp = (expected); \ + typeof (*object) _des = (desired); \ + bool ret; \ + vlc_global_lock(VLC_ATOMIC_MUTEX); \ + ret = *_obj == *_exp; \ + if (ret) \ + *_obj = _des; \ + else \ + *_exp = *_obj; \ + vlc_global_unlock(VLC_ATOMIC_MUTEX); \ + ret; \ +}) +# define atomic_compare_exchange_strong_explicit(object,expected,desired,order) \ + atomic_compare_exchange_strong(object, expected, desired) +# define atomic_compare_exchange_weak(object,expected,desired) \ + atomic_compare_exchange_strong(object, expected, desired) +# define atomic_compare_exchange_weak_explicit(object,expected,desired,order) \ + atomic_compare_exchange_weak(object, expected, desired) -static inline uintptr_t vlc_atomic_inc (vlc_atomic_t *atom) -{ - return vlc_atomic_add (atom, 1); -} +# define atomic_fetch_OP(object,desired,op) \ +({ \ + typeof (object) _obj = (object); \ + typeof (*object) _des = (desired); \ + typeof (*object) _old; \ + vlc_global_lock(VLC_ATOMIC_MUTEX); \ + _old = *_obj; \ + *_obj = (*_obj) op (_des); \ + vlc_global_unlock(VLC_ATOMIC_MUTEX); \ + _old; \ +}) -static inline uintptr_t vlc_atomic_dec (vlc_atomic_t *atom) -{ - return vlc_atomic_sub (atom, 1); -} +# define atomic_fetch_add(object,operand) \ + atomic_fetch_OP(object,operand,+) +# define atomic_fetch_add_explicit(object,operand,order) \ + atomic_fetch_add(object,operand) -static inline uintptr_t vlc_atomic_swap(vlc_atomic_t *atom, uintptr_t v) -{ - return atomic_exchange(&atom->u, v); -} +# define atomic_fetch_sub(object,operand) \ + atomic_fetch_OP(object,operand,-) +# define atomic_fetch_sub_explicit(object,operand,order) \ + atomic_fetch_sub(object,operand) + +# define atomic_fetch_or(object,operand) \ + atomic_fetch_OP(object,operand,|) +# define atomic_fetch_or_explicit(object,operand,order) \ + atomic_fetch_or(object,operand) + +# define atomic_fetch_xor(object,operand) \ + atomic_fetch_OP(object,operand,^) +# define atomic_fetch_xor_explicit(object,operand,order) \ + atomic_fetch_sub(object,operand) + +# define atomic_fetch_and(object,operand) \ + atomic_fetch_OP(object,operand,&) +# define atomic_fetch_and_explicit(object,operand,order) \ + atomic_fetch_and(object,operand) + +# define atomic_flag_test_and_set(object) \ + atomic_exchange(object, true) + +# define atomic_flag_test_and_set_explicit(object,order) \ + atomic_flag_test_and_set(object) -static inline uintptr_t vlc_atomic_compare_swap(vlc_atomic_t *atom, - uintptr_t u, uintptr_t v) +# define atomic_flag_clear(object) \ + atomic_store(object, false) + +# define atomic_flag_clear_explicit(object,order) \ + atomic_flag_clear(object) + +# elif defined (_MSC_VER) + +# include + +/*** Use the Interlocked API. ***/ + +/* Define macros in order to dispatch to the correct function depending on the type. + Several ranges are need because some operations are not implemented for all types. */ +# define atomic_type_dispatch_32_64(operation, object, ...) \ + (sizeof(*object) == 4 ? operation((LONG *)object, __VA_ARGS__) : \ + sizeof(*object) == 8 ? operation##64((LONGLONG *)object, __VA_ARGS__) : \ + (abort(), 0)) + +# define atomic_type_dispatch_16_64(operation, object, ...) \ + (sizeof(*object) == 2 ? operation##16((short *)object, __VA_ARGS__) : \ + atomic_type_dispatch_32_64(operation, object, __VA_ARGS__)) + +# define atomic_type_dispatch_8_64(operation, object, ...) \ + (sizeof(*object) == 1 ? operation##8((char *)object, __VA_ARGS__) : \ + atomic_type_dispatch_16_64(operation, object, __VA_ARGS__)) + +# define atomic_store(object,desired) \ + atomic_type_dispatch_16_64(InterlockedExchange, object, desired) +# define atomic_store_explicit(object,desired,order) \ + atomic_store(object, desired) + +# define atomic_load(object) \ + atomic_type_dispatch_16_64(InterlockedCompareExchange, object, 0, 0) +# define atomic_load_explicit(object,order) \ + atomic_load(object) + +# define atomic_exchange(object,desired) \ + atomic_type_dispatch_16_64(InterlockedExchange, object, desired) +# define atomic_exchange_explicit(object,desired,order) \ + atomic_exchange(object, desired) + +# define atomic_compare_exchange_strong(object,expected,desired) \ + atomic_type_dispatch_16_64(InterlockedCompareExchange, object, *expected, desired) == *expected +# define atomic_compare_exchange_strong_explicit(object,expected,desired,order) \ + atomic_compare_exchange_strong(object, expected, desired) +# define atomic_compare_exchange_weak(object,expected,desired) \ + atomic_compare_exchange_strong(object, expected, desired) +# define atomic_compare_exchange_weak_explicit(object,expected,desired,order) \ + atomic_compare_exchange_weak(object, expected, desired) + +# define atomic_fetch_add(object,operand) \ + atomic_type_dispatch_32_64(InterlockedExchangeAdd, object, operand) +# define atomic_fetch_add_explicit(object,operand,order) \ + atomic_fetch_add(object, operand) + +# define atomic_fetch_sub(object,operand) \ + atomic_type_dispatch_32_64(InterlockedExchangeAdd, object, -(LONGLONG)operand) +# define atomic_fetch_sub_explicit(object,operand,order) \ + atomic_fetch_sub(object, operand) + +# define atomic_fetch_or(object,operand) \ + atomic_type_dispatch_8_64(InterlockedOr, object, operand) +# define atomic_fetch_or_explicit(object,operand,order) \ + atomic_fetch_or(object, operand) + +# define atomic_fetch_xor(object,operand) \ + atomic_type_dispatch_8_64(InterlockedXor, object, operand) +# define atomic_fetch_xor_explicit(object,operand,order) \ + atomic_fetch_sub(object, operand) + +# define atomic_fetch_and(object,operand) \ + atomic_type_dispatch_8_64(InterlockedAnd, object, operand) +# define atomic_fetch_and_explicit(object,operand,order) \ + atomic_fetch_and(object, operand) + +# define atomic_flag_test_and_set(object) \ + atomic_exchange(object, true) + +# define atomic_flag_test_and_set_explicit(object,order) \ + atomic_flag_test_and_set(object) + +# define atomic_flag_clear(object) \ + atomic_store(object, false) + +# define atomic_flag_clear_explicit(object,order) \ + atomic_flag_clear(object) + +# else +# error FIXME: implement atomic operations for this compiler. +# endif +# endif + +typedef atomic_uint_least32_t vlc_atomic_float; + +static inline void vlc_atomic_init_float(vlc_atomic_float *var, float f) { - return atomic_compare_exchange_strong(&atom->u, &u, v); + union { float f; uint32_t i; } u; + u.f = f; + atomic_init(var, u.i); } /** Helper to retrieve a single precision from an atom. */ -static inline float vlc_atomic_getf(const vlc_atomic_t *atom) +static inline float vlc_atomic_load_float(vlc_atomic_float *atom) { - union { float f; uintptr_t i; } u; - u.i = vlc_atomic_get(atom); + union { float f; uint32_t i; } u; + u.i = atomic_load(atom); return u.f; } /** Helper to store a single precision into an atom. */ -static inline float vlc_atomic_setf(vlc_atomic_t *atom, float f) +static inline void vlc_atomic_store_float(vlc_atomic_float *atom, float f) { - union { float f; uintptr_t i; } u; + union { float f; uint32_t i; } u; u.f = f; - vlc_atomic_set(atom, u.i); - return f; + atomic_store(atom, u.i); } #endif