Home | History | Annotate | Download | only in util
      1 /**
      2  * Many similar implementations exist. See for example libwsbm
      3  * or the linux kernel include/atomic.h
      4  *
      5  * No copyright claimed on this file.
      6  *
      7  */
      8 
      9 #include "no_extern_c.h"
     10 
     11 #ifndef U_ATOMIC_H
     12 #define U_ATOMIC_H
     13 
     14 #include <stdbool.h>
     15 
     16 /* Favor OS-provided implementations.
     17  *
     18  * Where no OS-provided implementation is available, fall back to
     19  * locally coded assembly, compiler intrinsic or ultimately a
     20  * mutex-based implementation.
     21  */
     22 #if defined(__sun)
     23 #define PIPE_ATOMIC_OS_SOLARIS
     24 #elif defined(_MSC_VER)
     25 #define PIPE_ATOMIC_MSVC_INTRINSIC
     26 #elif defined(__GNUC__)
     27 #define PIPE_ATOMIC_GCC_INTRINSIC
     28 #else
     29 #error "Unsupported platform"
     30 #endif
     31 
     32 
     33 /* Implementation using GCC-provided synchronization intrinsics
     34  */
     35 #if defined(PIPE_ATOMIC_GCC_INTRINSIC)
     36 
     37 #define PIPE_ATOMIC "GCC Sync Intrinsics"
     38 
     39 #if defined(USE_GCC_ATOMIC_BUILTINS)
     40 
     41 /* The builtins with explicit memory model are available since GCC 4.7. */
     42 #define p_atomic_set(_v, _i) __atomic_store_n((_v), (_i), __ATOMIC_RELEASE)
     43 #define p_atomic_read(_v) __atomic_load_n((_v), __ATOMIC_ACQUIRE)
     44 #define p_atomic_dec_zero(v) (__atomic_sub_fetch((v), 1, __ATOMIC_ACQ_REL) == 0)
     45 #define p_atomic_inc(v) (void) __atomic_add_fetch((v), 1, __ATOMIC_ACQ_REL)
     46 #define p_atomic_dec(v) (void) __atomic_sub_fetch((v), 1, __ATOMIC_ACQ_REL)
     47 #define p_atomic_add(v, i) (void) __atomic_add_fetch((v), (i), __ATOMIC_ACQ_REL)
     48 #define p_atomic_inc_return(v) __atomic_add_fetch((v), 1, __ATOMIC_ACQ_REL)
     49 #define p_atomic_dec_return(v) __atomic_sub_fetch((v), 1, __ATOMIC_ACQ_REL)
     50 
     51 #else
     52 
     53 #define p_atomic_set(_v, _i) (*(_v) = (_i))
     54 #define p_atomic_read(_v) (*(_v))
     55 #define p_atomic_dec_zero(v) (__sync_sub_and_fetch((v), 1) == 0)
     56 #define p_atomic_inc(v) (void) __sync_add_and_fetch((v), 1)
     57 #define p_atomic_dec(v) (void) __sync_sub_and_fetch((v), 1)
     58 #define p_atomic_add(v, i) (void) __sync_add_and_fetch((v), (i))
     59 #define p_atomic_inc_return(v) __sync_add_and_fetch((v), 1)
     60 #define p_atomic_dec_return(v) __sync_sub_and_fetch((v), 1)
     61 
     62 #endif
     63 
     64 /* There is no __atomic_* compare and exchange that returns the current value.
     65  * Also, GCC 5.4 seems unable to optimize a compound statement expression that
     66  * uses an additional stack variable with __atomic_compare_exchange[_n].
     67  */
     68 #define p_atomic_cmpxchg(v, old, _new) \
     69    __sync_val_compare_and_swap((v), (old), (_new))
     70 
     71 #endif
     72 
     73 
     74 
     75 /* Unlocked version for single threaded environments, such as some
     76  * windows kernel modules.
     77  */
     78 #if defined(PIPE_ATOMIC_OS_UNLOCKED)
     79 
     80 #define PIPE_ATOMIC "Unlocked"
     81 
     82 #define p_atomic_set(_v, _i) (*(_v) = (_i))
     83 #define p_atomic_read(_v) (*(_v))
     84 #define p_atomic_dec_zero(_v) (p_atomic_dec_return(_v) == 0)
     85 #define p_atomic_inc(_v) ((void) p_atomic_inc_return(_v))
     86 #define p_atomic_dec(_v) ((void) p_atomic_dec_return(_v))
     87 #define p_atomic_add(_v, _i) (*(_v) = *(_v) + (_i))
     88 #define p_atomic_inc_return(_v) (++(*(_v)))
     89 #define p_atomic_dec_return(_v) (--(*(_v)))
     90 #define p_atomic_cmpxchg(_v, _old, _new) (*(_v) == (_old) ? (*(_v) = (_new), (_old)) : *(_v))
     91 
     92 #endif
     93 
     94 
     95 #if defined(PIPE_ATOMIC_MSVC_INTRINSIC)
     96 
     97 #define PIPE_ATOMIC "MSVC Intrinsics"
     98 
     99 /* We use the Windows header's Interlocked*64 functions instead of the
    100  * _Interlocked*64 intrinsics wherever we can, as support for the latter varies
    101  * with target CPU, whereas Windows headers take care of all portability
    102  * issues: using intrinsics where available, falling back to library
    103  * implementations where not.
    104  */
    105 #ifndef WIN32_LEAN_AND_MEAN
    106 #define WIN32_LEAN_AND_MEAN 1
    107 #endif
    108 #include <windows.h>
    109 #include <intrin.h>
    110 #include <assert.h>
    111 
    112 /* MSVC supports decltype keyword, but it's only supported on C++ and doesn't
    113  * quite work here; and if a C++-only solution is worthwhile, then it would be
    114  * better to use templates / function overloading, instead of decltype magic.
    115  * Therefore, we rely on implicit casting to LONGLONG for the functions that return
    116  */
    117 
    118 #define p_atomic_set(_v, _i) (*(_v) = (_i))
    119 #define p_atomic_read(_v) (*(_v))
    120 
    121 #define p_atomic_dec_zero(_v) \
    122    (p_atomic_dec_return(_v) == 0)
    123 
    124 #define p_atomic_inc(_v) \
    125    ((void) p_atomic_inc_return(_v))
    126 
    127 #define p_atomic_inc_return(_v) (\
    128    sizeof *(_v) == sizeof(short)   ? _InterlockedIncrement16((short *)  (_v)) : \
    129    sizeof *(_v) == sizeof(long)    ? _InterlockedIncrement  ((long *)   (_v)) : \
    130    sizeof *(_v) == sizeof(__int64) ? InterlockedIncrement64 ((__int64 *)(_v)) : \
    131                                      (assert(!"should not get here"), 0))
    132 
    133 #define p_atomic_dec(_v) \
    134    ((void) p_atomic_dec_return(_v))
    135 
    136 #define p_atomic_dec_return(_v) (\
    137    sizeof *(_v) == sizeof(short)   ? _InterlockedDecrement16((short *)  (_v)) : \
    138    sizeof *(_v) == sizeof(long)    ? _InterlockedDecrement  ((long *)   (_v)) : \
    139    sizeof *(_v) == sizeof(__int64) ? InterlockedDecrement64 ((__int64 *)(_v)) : \
    140                                      (assert(!"should not get here"), 0))
    141 
    142 #define p_atomic_add(_v, _i) (\
    143    sizeof *(_v) == sizeof(char)    ? _InterlockedExchangeAdd8 ((char *)   (_v), (_i)) : \
    144    sizeof *(_v) == sizeof(short)   ? _InterlockedExchangeAdd16((short *)  (_v), (_i)) : \
    145    sizeof *(_v) == sizeof(long)    ? _InterlockedExchangeAdd  ((long *)   (_v), (_i)) : \
    146    sizeof *(_v) == sizeof(__int64) ? InterlockedExchangeAdd64((__int64 *)(_v), (_i)) : \
    147                                      (assert(!"should not get here"), 0))
    148 
    149 #define p_atomic_cmpxchg(_v, _old, _new) (\
    150    sizeof *(_v) == sizeof(char)    ? _InterlockedCompareExchange8 ((char *)   (_v), (char)   (_new), (char)   (_old)) : \
    151    sizeof *(_v) == sizeof(short)   ? _InterlockedCompareExchange16((short *)  (_v), (short)  (_new), (short)  (_old)) : \
    152    sizeof *(_v) == sizeof(long)    ? _InterlockedCompareExchange  ((long *)   (_v), (long)   (_new), (long)   (_old)) : \
    153    sizeof *(_v) == sizeof(__int64) ? InterlockedCompareExchange64 ((__int64 *)(_v), (__int64)(_new), (__int64)(_old)) : \
    154                                      (assert(!"should not get here"), 0))
    155 
    156 #endif
    157 
    158 #if defined(PIPE_ATOMIC_OS_SOLARIS)
    159 
    160 #define PIPE_ATOMIC "Solaris OS atomic functions"
    161 
    162 #include <atomic.h>
    163 #include <assert.h>
    164 
    165 #define p_atomic_set(_v, _i) (*(_v) = (_i))
    166 #define p_atomic_read(_v) (*(_v))
    167 
    168 #define p_atomic_dec_zero(v) (\
    169    sizeof(*v) == sizeof(uint8_t)  ? atomic_dec_8_nv ((uint8_t  *)(v)) == 0 : \
    170    sizeof(*v) == sizeof(uint16_t) ? atomic_dec_16_nv((uint16_t *)(v)) == 0 : \
    171    sizeof(*v) == sizeof(uint32_t) ? atomic_dec_32_nv((uint32_t *)(v)) == 0 : \
    172    sizeof(*v) == sizeof(uint64_t) ? atomic_dec_64_nv((uint64_t *)(v)) == 0 : \
    173                                     (assert(!"should not get here"), 0))
    174 
    175 #define p_atomic_inc(v) (void) (\
    176    sizeof(*v) == sizeof(uint8_t)  ? atomic_inc_8 ((uint8_t  *)(v)) : \
    177    sizeof(*v) == sizeof(uint16_t) ? atomic_inc_16((uint16_t *)(v)) : \
    178    sizeof(*v) == sizeof(uint32_t) ? atomic_inc_32((uint32_t *)(v)) : \
    179    sizeof(*v) == sizeof(uint64_t) ? atomic_inc_64((uint64_t *)(v)) : \
    180                                     (assert(!"should not get here"), 0))
    181 
    182 #define p_atomic_inc_return(v) ((__typeof(*v)) \
    183    sizeof(*v) == sizeof(uint8_t)  ? atomic_inc_8_nv ((uint8_t  *)(v)) : \
    184    sizeof(*v) == sizeof(uint16_t) ? atomic_inc_16_nv((uint16_t *)(v)) : \
    185    sizeof(*v) == sizeof(uint32_t) ? atomic_inc_32_nv((uint32_t *)(v)) : \
    186    sizeof(*v) == sizeof(uint64_t) ? atomic_inc_64_nv((uint64_t *)(v)) : \
    187                                     (assert(!"should not get here"), 0))
    188 
    189 #define p_atomic_dec(v) ((void) \
    190    sizeof(*v) == sizeof(uint8_t)  ? atomic_dec_8 ((uint8_t  *)(v)) : \
    191    sizeof(*v) == sizeof(uint16_t) ? atomic_dec_16((uint16_t *)(v)) : \
    192    sizeof(*v) == sizeof(uint32_t) ? atomic_dec_32((uint32_t *)(v)) : \
    193    sizeof(*v) == sizeof(uint64_t) ? atomic_dec_64((uint64_t *)(v)) : \
    194                                     (assert(!"should not get here"), 0))
    195 
    196 #define p_atomic_dec_return(v) ((__typeof(*v)) \
    197    sizeof(*v) == sizeof(uint8_t)  ? atomic_dec_8_nv ((uint8_t  *)(v)) : \
    198    sizeof(*v) == sizeof(uint16_t) ? atomic_dec_16_nv((uint16_t *)(v)) : \
    199    sizeof(*v) == sizeof(uint32_t) ? atomic_dec_32_nv((uint32_t *)(v)) : \
    200    sizeof(*v) == sizeof(uint64_t) ? atomic_dec_64_nv((uint64_t *)(v)) : \
    201                                     (assert(!"should not get here"), 0))
    202 
    203 #define p_atomic_add(v, i) ((void)				     \
    204    sizeof(*v) == sizeof(uint8_t)  ? atomic_add_8 ((uint8_t  *)(v), (i)) : \
    205    sizeof(*v) == sizeof(uint16_t) ? atomic_add_16((uint16_t *)(v), (i)) : \
    206    sizeof(*v) == sizeof(uint32_t) ? atomic_add_32((uint32_t *)(v), (i)) : \
    207    sizeof(*v) == sizeof(uint64_t) ? atomic_add_64((uint64_t *)(v), (i)) : \
    208                                     (assert(!"should not get here"), 0))
    209 
    210 #define p_atomic_cmpxchg(v, old, _new) ((__typeof(*v)) \
    211    sizeof(*v) == sizeof(uint8_t)  ? atomic_cas_8 ((uint8_t  *)(v), (uint8_t )(old), (uint8_t )(_new)) : \
    212    sizeof(*v) == sizeof(uint16_t) ? atomic_cas_16((uint16_t *)(v), (uint16_t)(old), (uint16_t)(_new)) : \
    213    sizeof(*v) == sizeof(uint32_t) ? atomic_cas_32((uint32_t *)(v), (uint32_t)(old), (uint32_t)(_new)) : \
    214    sizeof(*v) == sizeof(uint64_t) ? atomic_cas_64((uint64_t *)(v), (uint64_t)(old), (uint64_t)(_new)) : \
    215                                     (assert(!"should not get here"), 0))
    216 
    217 #endif
    218 
    219 #ifndef PIPE_ATOMIC
    220 #error "No pipe_atomic implementation selected"
    221 #endif
    222 
    223 
    224 
    225 #endif /* U_ATOMIC_H */
    226