jatomic.hpp 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. /*##############################################################################
  2. HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems®.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ############################################################################## */
  13. #ifndef JATOMIC_HPP
  14. #define JATOMIC_HPP
  15. #include "platform.h"
  16. #ifdef _WIN32
  17. inline static void spinPause() { YieldProcessor(); }
  18. #elif defined(_ARCH_X86_64_) || defined(_ARCH_X86_)
  19. # include "x86intrin.h"
  20. # if defined(_ARCH_X86_)
  21. inline static void spinPause() { __pause(); }
  22. // or could use
  23. // __asm__ __volatile__ ("rep; nop" ::: "memory");
  24. // __asm__ __volatile__ ("pause" ::: "memory");
  25. # else
  26. inline static void spinPause() { _mm_pause(); }
  27. # endif
  28. #else
  29. // _ARCH_ARM64_ || _ARCH_ARM32_
  30. // inline static void spinPause() { __nop(); }
  31. // _ARCH_PPC64EL_
  32. // __asm__ __volatile__ ("or 0,0,0");
  33. inline static void spinPause() { }
  34. #endif
  35. #ifdef _WIN32
  36. #include <intrin.h>
  37. extern "C"
  38. {
  39. LONG __cdecl _InterlockedIncrement(LONG volatile *Addend);
  40. LONG __cdecl _InterlockedDecrement(LONG volatile *Addend);
  41. LONG __cdecl _InterlockedCompareExchange(LONG volatile * Dest, LONG Exchange, LONG Comp);
  42. }
  43. #pragma intrinsic (_InterlockedCompareExchange)
  44. #define InterlockedCompareExchange _InterlockedCompareExchange
  45. #pragma intrinsic (_InterlockedIncrement)
  46. #define InterlockedIncrement _InterlockedIncrement
  47. #pragma intrinsic (_InterlockedDecrement)
  48. #define InterlockedDecrement _InterlockedDecrement
  49. #pragma intrinsic (_InterlockedExchangeAdd)
  50. #define InterlockedExchangeAdd _InterlockedExchangeAdd
  51. typedef volatile long atomic_t;
  52. #define ATOMIC_INIT(i) (i)
  53. #define atomic_inc(v) InterlockedIncrement(v)
  54. #define atomic_inc_and_test(v) (InterlockedIncrement(v) == 0)
  55. #define atomic_dec(v) InterlockedDecrement(v)
  56. #define atomic_dec_and_test(v) (InterlockedDecrement(v) == 0)
  57. #define atomic_dec_and_read(v) InterlockedDecrement(v)
  58. #define atomic_read(v) (*v)
  59. #define atomic_set(v,i) ((*v) = (i))
  60. #define atomic_xchg(i, v) InterlockedExchange(v, i)
  61. #define atomic_add(v,i) InterlockedExchangeAdd(v,i)
  62. #define atomic_add_and_read(v,i) InterlockedAdd(v,i)
  63. #define atomic_add_exchange(v, i) InterlockedExchangeAdd(v,i)
  64. #define atomic_xchg_ptr(p, v) InterlockedExchangePointer(v,p)
  65. #if defined (_MSC_VER) && (_MSC_VER <= 1200)
  66. #define atomic_cas(v,newvalue,expectedvalue) (InterlockedCompareExchange((PVOID *)(v),(PVOID)(long)(newvalue),(PVOID)(long)(expectedvalue))==(PVOID)(long)(expectedvalue))
  67. #define atomic_cas_ptr(v, newvalue,expectedvalue) atomic_cas(v,(long)newvalue,(long)expectedvalue)
  68. #else
  69. #define atomic_cas(v,newvalue,expectedvalue) (InterlockedCompareExchange(v,newvalue,expectedvalue)==expectedvalue)
  70. #define atomic_cas_ptr(v, newvalue,expectedvalue) (InterlockedCompareExchangePointer(v,newvalue,expectedvalue)==expectedvalue)
  71. #endif
  72. //Used to prevent a compiler reordering volatile and non-volatile loads/stores
  73. #define compiler_memory_barrier() _ReadWriteBarrier()
  74. #define atomic_acquire(v) atomic_cas(v, 1, 0)
  75. #define atomic_release(v) { compiler_memory_barrier(); atomic_set(v, 0); }
  76. #elif defined(__GNUC__)
  77. typedef struct { volatile int counter; } atomic_t;
  78. #define ATOMIC_INIT(i) { (i) }
  79. #define atomic_read(v) ((v)->counter)
  80. #define atomic_set(v,i) (((v)->counter) = (i))
  81. static __inline__ bool atomic_dec_and_test(atomic_t *v)
  82. {
  83. // returns (--*v==0)
  84. return (__sync_add_and_fetch(&v->counter,-1)==0);
  85. }
  86. static __inline__ bool atomic_inc_and_test(atomic_t *v)
  87. {
  88. // returns (++*v==0)
  89. return (__sync_add_and_fetch(&v->counter,1)==0);
  90. }
  91. static __inline__ void atomic_inc(atomic_t *v)
  92. {
  93. // (*v)++
  94. __sync_add_and_fetch(&v->counter,1);
  95. }
  96. static __inline__ void atomic_dec(atomic_t *v)
  97. {
  98. // (*v)--
  99. __sync_add_and_fetch(&v->counter,-1);
  100. }
  101. static __inline__ int atomic_dec_and_read(atomic_t *v)
  102. {
  103. // (*v)--, return *v;
  104. return __sync_add_and_fetch(&v->counter,-1);
  105. }
  106. static __inline__ int atomic_xchg(int i, atomic_t *v)
  107. {
  108. // int ret = *v; *v = i; return v;
  109. return __sync_lock_test_and_set(&v->counter,i); // actually an xchg
  110. }
  111. static __inline__ void atomic_add(atomic_t *v,int i)
  112. {
  113. // (*v) += i;
  114. __sync_add_and_fetch(&v->counter,i);
  115. }
  116. static __inline__ int atomic_add_and_read(atomic_t *v,int i)
  117. {
  118. // (*v) += i; return *v;
  119. return __sync_add_and_fetch(&v->counter,i);
  120. }
  121. static __inline__ int atomic_add_exchange(atomic_t *v,int i)
  122. {
  123. // int ret = *v; (*v) += i; return ret;
  124. return __sync_fetch_and_add(&v->counter,i);
  125. }
  126. static __inline__ bool atomic_cas(atomic_t *v,int newvalue, int expectedvalue)
  127. {
  128. // bool ret = (*v==expectedvalue); if (ret) *v = newvalue; return ret;
  129. return __sync_bool_compare_and_swap(&v->counter, expectedvalue, newvalue);
  130. }
  131. static __inline__ void * atomic_xchg_ptr(void *p, void **v)
  132. {
  133. // void * ret = *v; (*v) = p; return ret;
  134. return (void *)__sync_lock_test_and_set((memsize_t *)v,(memsize_t)p);
  135. }
  136. static __inline__ bool atomic_cas_ptr(void **v,void *newvalue, void *expectedvalue)
  137. {
  138. // bool ret = (*v==expectedvalue); if (ret) *v = newvalue; return ret;
  139. return __sync_bool_compare_and_swap((memsize_t *)v, (memsize_t)expectedvalue, (memsize_t)newvalue);
  140. }
  141. #define compiler_memory_barrier() asm volatile("": : :"memory")
  142. static __inline__ bool atomic_acquire(atomic_t *v)
  143. {
  144. #if defined(_ARCH_X86_64_) || defined(_ARCH_X86_)
  145. //For some reason gcc targeting x86 generates code for atomic_cas() that requires fewer registers
  146. return atomic_cas(v, 1, 0);
  147. #else
  148. return __sync_lock_test_and_set(&v->counter, 1) == 0;
  149. #endif
  150. }
  151. static __inline__ void atomic_release(atomic_t *v)
  152. {
  153. #if defined(_ARCH_X86_64_) || defined(_ARCH_X86_)
  154. //x86 has a strong memory model, so the following code is sufficient, and some older gcc compilers generate
  155. //an unnecessary mfence instruction, so for x86 use the following which generates better code.
  156. compiler_memory_barrier();
  157. atomic_set(v, 0);
  158. #else
  159. __sync_lock_release(&v->counter);
  160. #endif
  161. }
  162. #else // other unix
  163. //Truely awful implementations of atomic operations...
  164. typedef volatile int atomic_t;
  165. int jlib_decl poor_atomic_dec_and_read(atomic_t * v);
  166. bool jlib_decl poor_atomic_inc_and_test(atomic_t * v);
  167. int jlib_decl poor_atomic_xchg(int i, atomic_t * v);
  168. void jlib_decl poor_atomic_add(atomic_t * v, int i);
  169. int jlib_decl poor_atomic_add_and_read(atomic_t * v, int i);
  170. int jlib_decl poor_atomic_add_exchange(atomic_t * v, int i);
  171. bool jlib_decl poor_atomic_cas(atomic_t * v, int newvalue, int expectedvalue);
  172. void jlib_decl *poor_atomic_xchg_ptr(void *p, void **v);
  173. bool jlib_decl poor_atomic_cas_ptr(void ** v, void *newvalue, void *expectedvalue);
  174. void jlib_decl poor_compiler_memory_barrier();
  175. #define ATOMIC_INIT(i) (i)
  176. #define atomic_inc(v) (void)poor_atomic_inc_and_test(v)
  177. #define atomic_inc_and_test(v) poor_atomic_inc_and_test(v)
  178. #define atomic_dec(v) (void)poor_atomic_dec_and_read(v)
  179. #define atomic_dec_and_read(v) poor_atomic_dec_and_read(v)
  180. #define atomic_dec_and_test(v) (poor_atomic_dec_and_read(v)==0)
  181. #define atomic_read(v) (*v)
  182. #define atomic_set(v,i) ((*v) = (i))
  183. #define atomic_xchg(i, v) poor_atomic_xchg(i, v)
  184. #define atomic_add(v,i) poor_atomic_add(v, i)
  185. #define atomic_add_and_read(v,i) poor_atomic_add_and_read(v, i)
  186. #define atomic_add_exchange(v, i) poor_atomic_add_exchange(v, i)
  187. #define atomic_cas(v,newvalue,expectedvalue) poor_atomic_cas(v,newvalue,expectedvalue)
  188. #define atomic_xchg_ptr(p, v) poor_atomic_xchg_ptr(p, v)
  189. #define atomic_cas_ptr(v,newvalue,expectedvalue) poor_atomic_cas_ptr(v,newvalue,expectedvalue)
  190. #define compiler_memory_barrier() poor_compiler_memory_barrier()
  191. #define atomic_acquire(v) atomic_cas(v, 1, 0)
  192. #define atomic_release(v) { compiler_memory_barrier(); atomic_set(v, 0); }
  193. #endif
  194. #endif