atomic_pointer.h 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file. See the AUTHORS file for names of contributors.
  4. // AtomicPointer provides storage for a lock-free pointer.
  5. // Platform-dependent implementation of AtomicPointer:
  6. // - If the platform provides a cheap barrier, we use it with raw pointers
  7. // - If <atomic> is present (on newer versions of gcc, it is), we use
  8. // a <atomic>-based AtomicPointer. However we prefer the memory
  9. // barrier based version, because at least on a gcc 4.4 32-bit build
  10. // on linux, we have encountered a buggy <atomic> implementation.
  11. // Also, some <atomic> implementations are much slower than a memory-barrier
  12. // based implementation (~16ns for <atomic> based acquire-load vs. ~1ns for
  13. // a barrier based acquire-load).
  14. // This code is based on atomicops-internals-* in Google's perftools:
  15. // http://code.google.com/p/google-perftools/source/browse/#svn%2Ftrunk%2Fsrc%2Fbase
  16. #ifndef PORT_ATOMIC_POINTER_H_
  17. #define PORT_ATOMIC_POINTER_H_
  18. #include <stdint.h>
  19. #ifdef LEVELDB_ATOMIC_PRESENT
  20. #include <atomic>
  21. #endif
  22. #ifdef OS_WIN
  23. #include <windows.h>
  24. #endif
  25. #ifdef OS_MACOSX
  26. #include <libkern/OSAtomic.h>
  27. #endif
  28. #if defined(_M_X64) || defined(__x86_64__)
  29. #define ARCH_CPU_X86_FAMILY 1
  30. #elif defined(_M_IX86) || defined(__i386__) || defined(__i386)
  31. #define ARCH_CPU_X86_FAMILY 1
  32. #elif defined(__ARMEL__)
  33. #define ARCH_CPU_ARM_FAMILY 1
  34. #elif defined(__aarch64__)
  35. #define ARCH_CPU_ARM64_FAMILY 1
  36. #elif defined(__ppc__) || defined(__powerpc__) || defined(__powerpc64__)
  37. #define ARCH_CPU_PPC_FAMILY 1
  38. #endif
  39. namespace leveldb {
  40. namespace port {
  41. // Define MemoryBarrier() if available
  42. // Windows on x86
  43. #if defined(OS_WIN) && defined(COMPILER_MSVC) && defined(ARCH_CPU_X86_FAMILY)
  44. // windows.h already provides a MemoryBarrier(void) macro
  45. // http://msdn.microsoft.com/en-us/library/ms684208(v=vs.85).aspx
  46. #define LEVELDB_HAVE_MEMORY_BARRIER
  47. // Mac OS
  48. #elif defined(OS_MACOSX)
  49. inline void MemoryBarrier() {
  50. OSMemoryBarrier();
  51. }
  52. #define LEVELDB_HAVE_MEMORY_BARRIER
  53. // Gcc on x86
  54. #elif defined(ARCH_CPU_X86_FAMILY) && defined(__GNUC__)
  55. inline void MemoryBarrier() {
  56. // See http://gcc.gnu.org/ml/gcc/2003-04/msg01180.html for a discussion on
  57. // this idiom. Also see http://en.wikipedia.org/wiki/Memory_ordering.
  58. __asm__ __volatile__("" : : : "memory");
  59. }
  60. #define LEVELDB_HAVE_MEMORY_BARRIER
  61. // Sun Studio
  62. #elif defined(ARCH_CPU_X86_FAMILY) && defined(__SUNPRO_CC)
  63. inline void MemoryBarrier() {
  64. // See http://gcc.gnu.org/ml/gcc/2003-04/msg01180.html for a discussion on
  65. // this idiom. Also see http://en.wikipedia.org/wiki/Memory_ordering.
  66. asm volatile("" : : : "memory");
  67. }
  68. #define LEVELDB_HAVE_MEMORY_BARRIER
  69. // ARM Linux
  70. #elif defined(ARCH_CPU_ARM_FAMILY) && defined(__linux__)
  71. typedef void (*LinuxKernelMemoryBarrierFunc)(void);
  72. // The Linux ARM kernel provides a highly optimized device-specific memory
  73. // barrier function at a fixed memory address that is mapped in every
  74. // user-level process.
  75. //
  76. // This beats using CPU-specific instructions which are, on single-core
  77. // devices, un-necessary and very costly (e.g. ARMv7-A "dmb" takes more
  78. // than 180ns on a Cortex-A8 like the one on a Nexus One). Benchmarking
  79. // shows that the extra function call cost is completely negligible on
  80. // multi-core devices.
  81. //
  82. inline void MemoryBarrier() {
  83. (*(LinuxKernelMemoryBarrierFunc)0xffff0fa0)();
  84. }
  85. #define LEVELDB_HAVE_MEMORY_BARRIER
  86. // ARM64
  87. #elif defined(ARCH_CPU_ARM64_FAMILY)
  88. inline void MemoryBarrier() {
  89. asm volatile("dmb sy" : : : "memory");
  90. }
  91. #define LEVELDB_HAVE_MEMORY_BARRIER
  92. // PPC
  93. #elif defined(ARCH_CPU_PPC_FAMILY) && defined(__GNUC__)
  94. inline void MemoryBarrier() {
  95. // TODO for some powerpc expert: is there a cheaper suitable variant?
  96. // Perhaps by having separate barriers for acquire and release ops.
  97. asm volatile("sync" : : : "memory");
  98. }
  99. #define LEVELDB_HAVE_MEMORY_BARRIER
  100. #endif
  101. // AtomicPointer built using platform-specific MemoryBarrier()
  102. #if defined(LEVELDB_HAVE_MEMORY_BARRIER)
  103. class AtomicPointer {
  104. private:
  105. void* rep_;
  106. public:
  107. AtomicPointer() { }
  108. explicit AtomicPointer(void* p) : rep_(p) {}
  109. inline void* NoBarrier_Load() const { return rep_; }
  110. inline void NoBarrier_Store(void* v) { rep_ = v; }
  111. inline void* Acquire_Load() const {
  112. void* result = rep_;
  113. MemoryBarrier();
  114. return result;
  115. }
  116. inline void Release_Store(void* v) {
  117. MemoryBarrier();
  118. rep_ = v;
  119. }
  120. };
  121. // AtomicPointer based on <cstdatomic>
  122. #elif defined(LEVELDB_ATOMIC_PRESENT)
  123. class AtomicPointer {
  124. private:
  125. std::atomic<void*> rep_;
  126. public:
  127. AtomicPointer() { }
  128. explicit AtomicPointer(void* v) : rep_(v) { }
  129. inline void* Acquire_Load() const {
  130. return rep_.load(std::memory_order_acquire);
  131. }
  132. inline void Release_Store(void* v) {
  133. rep_.store(v, std::memory_order_release);
  134. }
  135. inline void* NoBarrier_Load() const {
  136. return rep_.load(std::memory_order_relaxed);
  137. }
  138. inline void NoBarrier_Store(void* v) {
  139. rep_.store(v, std::memory_order_relaxed);
  140. }
  141. };
  142. // Atomic pointer based on sparc memory barriers
  143. #elif defined(__sparcv9) && defined(__GNUC__)
  144. class AtomicPointer {
  145. private:
  146. void* rep_;
  147. public:
  148. AtomicPointer() { }
  149. explicit AtomicPointer(void* v) : rep_(v) { }
  150. inline void* Acquire_Load() const {
  151. void* val;
  152. __asm__ __volatile__ (
  153. "ldx [%[rep_]], %[val] \n\t"
  154. "membar #LoadLoad|#LoadStore \n\t"
  155. : [val] "=r" (val)
  156. : [rep_] "r" (&rep_)
  157. : "memory");
  158. return val;
  159. }
  160. inline void Release_Store(void* v) {
  161. __asm__ __volatile__ (
  162. "membar #LoadStore|#StoreStore \n\t"
  163. "stx %[v], [%[rep_]] \n\t"
  164. :
  165. : [rep_] "r" (&rep_), [v] "r" (v)
  166. : "memory");
  167. }
  168. inline void* NoBarrier_Load() const { return rep_; }
  169. inline void NoBarrier_Store(void* v) { rep_ = v; }
  170. };
  171. // Atomic pointer based on ia64 acq/rel
  172. #elif defined(__ia64) && defined(__GNUC__)
  173. class AtomicPointer {
  174. private:
  175. void* rep_;
  176. public:
  177. AtomicPointer() { }
  178. explicit AtomicPointer(void* v) : rep_(v) { }
  179. inline void* Acquire_Load() const {
  180. void* val ;
  181. __asm__ __volatile__ (
  182. "ld8.acq %[val] = [%[rep_]] \n\t"
  183. : [val] "=r" (val)
  184. : [rep_] "r" (&rep_)
  185. : "memory"
  186. );
  187. return val;
  188. }
  189. inline void Release_Store(void* v) {
  190. __asm__ __volatile__ (
  191. "st8.rel [%[rep_]] = %[v] \n\t"
  192. :
  193. : [rep_] "r" (&rep_), [v] "r" (v)
  194. : "memory"
  195. );
  196. }
  197. inline void* NoBarrier_Load() const { return rep_; }
  198. inline void NoBarrier_Store(void* v) { rep_ = v; }
  199. };
  200. // We have neither MemoryBarrier(), nor <atomic>
  201. #else
  202. #error Please implement AtomicPointer for this platform.
  203. #endif
  204. #undef LEVELDB_HAVE_MEMORY_BARRIER
  205. #undef ARCH_CPU_X86_FAMILY
  206. #undef ARCH_CPU_ARM_FAMILY
  207. #undef ARCH_CPU_ARM64_FAMILY
  208. #undef ARCH_CPU_PPC_FAMILY
  209. } // namespace port
  210. } // namespace leveldb
  211. #endif // PORT_ATOMIC_POINTER_H_