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 #ifndef U_ATOMIC_H
     10 #define U_ATOMIC_H
     11 
     12 #include "pipe/p_compiler.h"
     13 #include "pipe/p_defines.h"
     14 
     15 /* Favor OS-provided implementations.
     16  *
     17  * Where no OS-provided implementation is available, fall back to
     18  * locally coded assembly, compiler intrinsic or ultimately a
     19  * mutex-based implementation.
     20  */
     21 #if defined(PIPE_OS_SOLARIS)
     22 #define PIPE_ATOMIC_OS_SOLARIS
     23 #elif defined(PIPE_CC_MSVC)
     24 #define PIPE_ATOMIC_MSVC_INTRINSIC
     25 #elif (defined(PIPE_CC_MSVC) && defined(PIPE_ARCH_X86))
     26 #define PIPE_ATOMIC_ASM_MSVC_X86
     27 #elif (defined(PIPE_CC_GCC) && defined(PIPE_ARCH_X86))
     28 #define PIPE_ATOMIC_ASM_GCC_X86
     29 #elif (defined(PIPE_CC_GCC) && defined(PIPE_ARCH_X86_64))
     30 #define PIPE_ATOMIC_ASM_GCC_X86_64
     31 #elif defined(PIPE_CC_GCC) && (PIPE_CC_GCC_VERSION >= 401)
     32 #define PIPE_ATOMIC_GCC_INTRINSIC
     33 #else
     34 #error "Unsupported platform"
     35 #endif
     36 
     37 
     38 #if defined(PIPE_ATOMIC_ASM_GCC_X86_64)
     39 #define PIPE_ATOMIC "GCC x86_64 assembly"
     40 
     41 #ifdef __cplusplus
     42 extern "C" {
     43 #endif
     44 
     45 #define p_atomic_set(_v, _i) (*(_v) = (_i))
     46 #define p_atomic_read(_v) (*(_v))
     47 
     48 static INLINE boolean
     49 p_atomic_dec_zero(int32_t *v)
     50 {
     51    unsigned char c;
     52 
     53    __asm__ __volatile__("lock; decl %0; sete %1":"+m"(*v), "=qm"(c)
     54 			::"memory");
     55 
     56    return c != 0;
     57 }
     58 
     59 static INLINE void
     60 p_atomic_inc(int32_t *v)
     61 {
     62    __asm__ __volatile__("lock; incl %0":"+m"(*v));
     63 }
     64 
     65 static INLINE void
     66 p_atomic_dec(int32_t *v)
     67 {
     68    __asm__ __volatile__("lock; decl %0":"+m"(*v));
     69 }
     70 
     71 static INLINE int32_t
     72 p_atomic_cmpxchg(int32_t *v, int32_t old, int32_t _new)
     73 {
     74    return __sync_val_compare_and_swap(v, old, _new);
     75 }
     76 
     77 #ifdef __cplusplus
     78 }
     79 #endif
     80 
     81 #endif /* PIPE_ATOMIC_ASM_GCC_X86_64 */
     82 
     83 
     84 #if defined(PIPE_ATOMIC_ASM_GCC_X86)
     85 
     86 #define PIPE_ATOMIC "GCC x86 assembly"
     87 
     88 #ifdef __cplusplus
     89 extern "C" {
     90 #endif
     91 
     92 #define p_atomic_set(_v, _i) (*(_v) = (_i))
     93 #define p_atomic_read(_v) (*(_v))
     94 
     95 static INLINE boolean
     96 p_atomic_dec_zero(int32_t *v)
     97 {
     98    unsigned char c;
     99 
    100    __asm__ __volatile__("lock; decl %0; sete %1":"+m"(*v), "=qm"(c)
    101 			::"memory");
    102 
    103    return c != 0;
    104 }
    105 
    106 static INLINE void
    107 p_atomic_inc(int32_t *v)
    108 {
    109    __asm__ __volatile__("lock; incl %0":"+m"(*v));
    110 }
    111 
    112 static INLINE void
    113 p_atomic_dec(int32_t *v)
    114 {
    115    __asm__ __volatile__("lock; decl %0":"+m"(*v));
    116 }
    117 
    118 static INLINE int32_t
    119 p_atomic_cmpxchg(int32_t *v, int32_t old, int32_t _new)
    120 {
    121    return __sync_val_compare_and_swap(v, old, _new);
    122 }
    123 
    124 #ifdef __cplusplus
    125 }
    126 #endif
    127 
    128 #endif
    129 
    130 
    131 
    132 /* Implementation using GCC-provided synchronization intrinsics
    133  */
    134 #if defined(PIPE_ATOMIC_GCC_INTRINSIC)
    135 
    136 #define PIPE_ATOMIC "GCC Sync Intrinsics"
    137 
    138 #ifdef __cplusplus
    139 extern "C" {
    140 #endif
    141 
    142 #define p_atomic_set(_v, _i) (*(_v) = (_i))
    143 #define p_atomic_read(_v) (*(_v))
    144 
    145 static INLINE boolean
    146 p_atomic_dec_zero(int32_t *v)
    147 {
    148    return (__sync_sub_and_fetch(v, 1) == 0);
    149 }
    150 
    151 static INLINE void
    152 p_atomic_inc(int32_t *v)
    153 {
    154    (void) __sync_add_and_fetch(v, 1);
    155 }
    156 
    157 static INLINE void
    158 p_atomic_dec(int32_t *v)
    159 {
    160    (void) __sync_sub_and_fetch(v, 1);
    161 }
    162 
    163 static INLINE int32_t
    164 p_atomic_cmpxchg(int32_t *v, int32_t old, int32_t _new)
    165 {
    166    return __sync_val_compare_and_swap(v, old, _new);
    167 }
    168 
    169 #ifdef __cplusplus
    170 }
    171 #endif
    172 
    173 #endif
    174 
    175 
    176 
    177 /* Unlocked version for single threaded environments, such as some
    178  * windows kernel modules.
    179  */
    180 #if defined(PIPE_ATOMIC_OS_UNLOCKED)
    181 
    182 #define PIPE_ATOMIC "Unlocked"
    183 
    184 #define p_atomic_set(_v, _i) (*(_v) = (_i))
    185 #define p_atomic_read(_v) (*(_v))
    186 #define p_atomic_dec_zero(_v) ((boolean) --(*(_v)))
    187 #define p_atomic_inc(_v) ((void) (*(_v))++)
    188 #define p_atomic_dec(_v) ((void) (*(_v))--)
    189 #define p_atomic_cmpxchg(_v, old, _new) (*(_v) == old ? *(_v) = (_new) : *(_v))
    190 
    191 #endif
    192 
    193 
    194 /* Locally coded assembly for MSVC on x86:
    195  */
    196 #if defined(PIPE_ATOMIC_ASM_MSVC_X86)
    197 
    198 #define PIPE_ATOMIC "MSVC x86 assembly"
    199 
    200 #ifdef __cplusplus
    201 extern "C" {
    202 #endif
    203 
    204 #define p_atomic_set(_v, _i) (*(_v) = (_i))
    205 #define p_atomic_read(_v) (*(_v))
    206 
    207 static INLINE boolean
    208 p_atomic_dec_zero(int32_t *v)
    209 {
    210    unsigned char c;
    211 
    212    __asm {
    213       mov       eax, [v]
    214       lock dec  dword ptr [eax]
    215       sete      byte ptr [c]
    216    }
    217 
    218    return c != 0;
    219 }
    220 
    221 static INLINE void
    222 p_atomic_inc(int32_t *v)
    223 {
    224    __asm {
    225       mov       eax, [v]
    226       lock inc  dword ptr [eax]
    227    }
    228 }
    229 
    230 static INLINE void
    231 p_atomic_dec(int32_t *v)
    232 {
    233    __asm {
    234       mov       eax, [v]
    235       lock dec  dword ptr [eax]
    236    }
    237 }
    238 
    239 static INLINE int32_t
    240 p_atomic_cmpxchg(int32_t *v, int32_t old, int32_t _new)
    241 {
    242    int32_t orig;
    243 
    244    __asm {
    245       mov ecx, [v]
    246       mov eax, [old]
    247       mov edx, [_new]
    248       lock cmpxchg [ecx], edx
    249       mov [orig], eax
    250    }
    251 
    252    return orig;
    253 }
    254 
    255 #ifdef __cplusplus
    256 }
    257 #endif
    258 
    259 #endif
    260 
    261 
    262 #if defined(PIPE_ATOMIC_MSVC_INTRINSIC)
    263 
    264 #define PIPE_ATOMIC "MSVC Intrinsics"
    265 
    266 #include <intrin.h>
    267 
    268 #pragma intrinsic(_InterlockedIncrement)
    269 #pragma intrinsic(_InterlockedDecrement)
    270 #pragma intrinsic(_InterlockedCompareExchange)
    271 
    272 #ifdef __cplusplus
    273 extern "C" {
    274 #endif
    275 
    276 #define p_atomic_set(_v, _i) (*(_v) = (_i))
    277 #define p_atomic_read(_v) (*(_v))
    278 
    279 static INLINE boolean
    280 p_atomic_dec_zero(int32_t *v)
    281 {
    282    return _InterlockedDecrement((long *)v) == 0;
    283 }
    284 
    285 static INLINE void
    286 p_atomic_inc(int32_t *v)
    287 {
    288    _InterlockedIncrement((long *)v);
    289 }
    290 
    291 static INLINE void
    292 p_atomic_dec(int32_t *v)
    293 {
    294    _InterlockedDecrement((long *)v);
    295 }
    296 
    297 static INLINE int32_t
    298 p_atomic_cmpxchg(int32_t *v, int32_t old, int32_t _new)
    299 {
    300    return _InterlockedCompareExchange((long *)v, _new, old);
    301 }
    302 
    303 #ifdef __cplusplus
    304 }
    305 #endif
    306 
    307 #endif
    308 
    309 #if defined(PIPE_ATOMIC_OS_SOLARIS)
    310 
    311 #define PIPE_ATOMIC "Solaris OS atomic functions"
    312 
    313 #include <atomic.h>
    314 
    315 #ifdef __cplusplus
    316 extern "C" {
    317 #endif
    318 
    319 #define p_atomic_set(_v, _i) (*(_v) = (_i))
    320 #define p_atomic_read(_v) (*(_v))
    321 
    322 static INLINE boolean
    323 p_atomic_dec_zero(int32_t *v)
    324 {
    325    uint32_t n = atomic_dec_32_nv((uint32_t *) v);
    326 
    327    return n != 0;
    328 }
    329 
    330 #define p_atomic_inc(_v) atomic_inc_32((uint32_t *) _v)
    331 #define p_atomic_dec(_v) atomic_dec_32((uint32_t *) _v)
    332 
    333 #define p_atomic_cmpxchg(_v, _old, _new) \
    334 	atomic_cas_32( (uint32_t *) _v, (uint32_t) _old, (uint32_t) _new)
    335 
    336 #ifdef __cplusplus
    337 }
    338 #endif
    339 
    340 #endif
    341 
    342 
    343 #ifndef PIPE_ATOMIC
    344 #error "No pipe_atomic implementation selected"
    345 #endif
    346 
    347 
    348 
    349 #endif /* U_ATOMIC_H */
    350