hb-atomic.hh (8967B)
1 /* 2 * Copyright © 2007 Chris Wilson 3 * Copyright © 2009,2010 Red Hat, Inc. 4 * Copyright © 2011,2012 Google, Inc. 5 * 6 * This is part of HarfBuzz, a text shaping library. 7 * 8 * Permission is hereby granted, without written agreement and without 9 * license or royalty fees, to use, copy, modify, and distribute this 10 * software and its documentation for any purpose, provided that the 11 * above copyright notice and the following two paragraphs appear in 12 * all copies of this software. 13 * 14 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR 15 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 16 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN 17 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 18 * DAMAGE. 19 * 20 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, 21 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 22 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS 23 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO 24 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 25 * 26 * Contributor(s): 27 * Chris Wilson <chris@chris-wilson.co.uk> 28 * Red Hat Author(s): Behdad Esfahbod 29 * Google Author(s): Behdad Esfahbod 30 */ 31 32 #ifndef HB_ATOMIC_HH 33 #define HB_ATOMIC_HH 34 35 #include "hb.hh" 36 #include "hb-meta.hh" 37 38 39 /* 40 * Atomic integers and pointers. 41 */ 42 43 /* We need external help for these */ 44 45 #if defined(hb_atomic_int_impl_add) \ 46 && defined(hb_atomic_ptr_impl_get) \ 47 && defined(hb_atomic_ptr_impl_cmpexch) 48 49 /* Defined externally, i.e. in config.h. */ 50 51 52 #elif !defined(HB_NO_MT) && defined(__ATOMIC_ACQUIRE) 53 54 /* C++11-style GCC primitives. We prefer these as they don't require linking to libstdc++ / libc++. */ 55 56 #define _hb_memory_barrier() __sync_synchronize () 57 58 #define hb_atomic_int_impl_add(AI, V) __atomic_fetch_add ((AI), (V), __ATOMIC_ACQ_REL) 59 #define hb_atomic_int_impl_set_relaxed(AI, V) __atomic_store_n ((AI), (V), __ATOMIC_RELAXED) 60 #define hb_atomic_int_impl_set(AI, V) __atomic_store_n ((AI), (V), __ATOMIC_RELEASE) 61 #define hb_atomic_int_impl_get_relaxed(AI) __atomic_load_n ((AI), __ATOMIC_RELAXED) 62 #define hb_atomic_int_impl_get(AI) __atomic_load_n ((AI), __ATOMIC_ACQUIRE) 63 64 #define hb_atomic_ptr_impl_set_relaxed(P, V) __atomic_store_n ((P), (V), __ATOMIC_RELAXED) 65 #define hb_atomic_ptr_impl_get_relaxed(P) __atomic_load_n ((P), __ATOMIC_RELAXED) 66 #define hb_atomic_ptr_impl_get(P) __atomic_load_n ((P), __ATOMIC_ACQUIRE) 67 static inline bool 68 _hb_atomic_ptr_impl_cmplexch (const void **P, const void *O_, const void *N) 69 { 70 const void *O = O_; // Need lvalue 71 return __atomic_compare_exchange_n ((void **) P, (void **) &O, (void *) N, true, __ATOMIC_ACQ_REL, __ATOMIC_RELAXED); 72 } 73 #define hb_atomic_ptr_impl_cmpexch(P,O,N) _hb_atomic_ptr_impl_cmplexch ((const void **) (P), (O), (N)) 74 75 76 #elif !defined(HB_NO_MT) 77 78 /* C++11 atomics. */ 79 80 #include <atomic> 81 82 #define HB_STL_ATOMIC_IMPL 83 84 #define _hb_memory_r_barrier() std::atomic_thread_fence(std::memory_order_acquire) 85 #define _hb_memory_w_barrier() std::atomic_thread_fence(std::memory_order_release) 86 87 #else /* defined(HB_NO_MT) */ 88 89 #define hb_atomic_int_impl_add(AI, V) ((*(AI) += (V)) - (V)) 90 #define _hb_memory_barrier() do {} while (0) 91 #define hb_atomic_ptr_impl_cmpexch(P,O,N) (* (void **) (P) == (void *) (O) ? (* (void **) (P) = (void *) (N), true) : false) 92 93 #endif 94 95 96 /* This should never be disabled, even under HB_NO_MT. 97 * except that MSVC gives me an internal compiler error, so disabled there. 98 * 99 * https://github.com/harfbuzz/harfbuzz/pull/4119 100 */ 101 #ifndef _hb_compiler_memory_r_barrier 102 #if defined(__ATOMIC_ACQUIRE) // gcc-like 103 static inline void _hb_compiler_memory_r_barrier () { asm volatile("": : :"memory"); } 104 #elif !defined(_MSC_VER) 105 #include <atomic> 106 #define _hb_compiler_memory_r_barrier() std::atomic_signal_fence (std::memory_order_acquire) 107 #else 108 static inline void _hb_compiler_memory_r_barrier () {} 109 #endif 110 #endif 111 112 113 114 #ifndef _hb_memory_r_barrier 115 #define _hb_memory_r_barrier() _hb_memory_barrier () 116 #endif 117 #ifndef _hb_memory_w_barrier 118 #define _hb_memory_w_barrier() _hb_memory_barrier () 119 #endif 120 #ifndef hb_atomic_int_impl_set_relaxed 121 #define hb_atomic_int_impl_set_relaxed(AI, V) (*(AI) = (V)) 122 #endif 123 #ifndef hb_atomic_int_impl_get_relaxed 124 #define hb_atomic_int_impl_get_relaxed(AI) (*(AI)) 125 #endif 126 127 #ifndef hb_atomic_ptr_impl_set_relaxed 128 #define hb_atomic_ptr_impl_set_relaxed(P, V) (*(P) = (V)) 129 #endif 130 #ifndef hb_atomic_ptr_impl_get_relaxed 131 #define hb_atomic_ptr_impl_get_relaxed(P) (*(P)) 132 #endif 133 #ifndef hb_atomic_int_impl_set 134 template <typename T> 135 inline void hb_atomic_int_impl_set (T *AI, T v) { _hb_memory_w_barrier (); *AI = v; } 136 #endif 137 #ifndef hb_atomic_int_impl_get 138 template <typename T> 139 inline T hb_atomic_int_impl_get (const T *AI) { T v = *AI; _hb_memory_r_barrier (); return v; } 140 #endif 141 #ifndef hb_atomic_ptr_impl_get 142 inline void *hb_atomic_ptr_impl_get (void ** const P) { void *v = *P; _hb_memory_r_barrier (); return v; } 143 #endif 144 145 #ifdef HB_STL_ATOMIC_IMPL 146 template <typename T> 147 struct hb_atomic_t 148 { 149 hb_atomic_t () = default; 150 constexpr hb_atomic_t (T v) : v (v) {} 151 constexpr hb_atomic_t (const hb_atomic_t& o) : v (o.get_relaxed ()) {} 152 constexpr hb_atomic_t (hb_atomic_t&& o) : v (o.get_relaxed ()) { o.set_relaxed ({}); } 153 154 hb_atomic_t &operator= (const hb_atomic_t& o) { set_relaxed (o.get_relaxed ()); return *this; } 155 hb_atomic_t &operator= (hb_atomic_t&& o){ set_relaxed (o.get_relaxed ()); o.set_relaxed ({}); return *this; } 156 hb_atomic_t &operator= (T v_) 157 { 158 set_relaxed (v_); 159 return *this; 160 } 161 operator T () const { return get_relaxed (); } 162 163 void set_relaxed (T v_) { v.store (v_, std::memory_order_relaxed); } 164 void set_release (T v_) { v.store (v_, std::memory_order_release); } 165 T get_relaxed () const { return v.load (std::memory_order_relaxed); } 166 T get_acquire () const { return v.load (std::memory_order_acquire); } 167 T inc () { return v.fetch_add (1, std::memory_order_acq_rel); } 168 T dec () { return v.fetch_add (-1, std::memory_order_acq_rel); } 169 170 int operator++ (int) { return inc (); } 171 int operator-- (int) { return dec (); } 172 173 friend void swap (hb_atomic_t &a, hb_atomic_t &b) noexcept 174 { 175 T v = a.get_acquire (); 176 a.set_relaxed (b.get_acquire ()); 177 b.set_relaxed (v); 178 } 179 180 std::atomic<T> v = 0; 181 }; 182 183 template <typename T> 184 struct hb_atomic_t<T *> 185 { 186 hb_atomic_t () = default; 187 constexpr hb_atomic_t (T *v) : v (v) {} 188 hb_atomic_t (const hb_atomic_t &other) = delete; 189 190 void init (T *v_ = nullptr) { set_relaxed (v_); } 191 void set_relaxed (T *v_) { v.store (v_, std::memory_order_relaxed); } 192 T *get_relaxed () const { return v.load (std::memory_order_relaxed); } 193 T *get_acquire () const { return v.load (std::memory_order_acquire); } 194 bool cmpexch (T *old, T *new_) { return v.compare_exchange_weak (old, new_, std::memory_order_acq_rel, std::memory_order_relaxed); } 195 196 operator bool () const { return get_acquire () != nullptr; } 197 T *operator->() const { return get_acquire (); } 198 template <typename C> 199 operator C * () const 200 { 201 return get_acquire (); 202 } 203 204 friend void swap (hb_atomic_t &a, hb_atomic_t &b) noexcept 205 { 206 T *p = a.get_acquire (); 207 a.set_relaxed (b.get_acquire ()); 208 b.set_relaxed (p); 209 } 210 211 std::atomic<T *> v = nullptr; 212 }; 213 214 #else 215 216 template <typename T> 217 struct hb_atomic_t 218 { 219 hb_atomic_t () = default; 220 constexpr hb_atomic_t (T v) : v (v) {} 221 222 hb_atomic_t& operator = (T v_) { set_relaxed (v_); return *this; } 223 operator T () const { return get_relaxed (); } 224 225 void set_relaxed (T v_) { hb_atomic_int_impl_set_relaxed (&v, v_); } 226 void set_release (T v_) { hb_atomic_int_impl_set (&v, v_); } 227 T get_relaxed () const { return hb_atomic_int_impl_get_relaxed (&v); } 228 T get_acquire () const { return hb_atomic_int_impl_get (&v); } 229 T inc () { return hb_atomic_int_impl_add (&v, 1); } 230 T dec () { return hb_atomic_int_impl_add (&v, -1); } 231 232 int operator ++ (int) { return inc (); } 233 int operator -- (int) { return dec (); } 234 235 T v = 0; 236 }; 237 238 template <typename T> 239 struct hb_atomic_t<T*> 240 { 241 hb_atomic_t () = default; 242 constexpr hb_atomic_t (T* v) : v (v) {} 243 hb_atomic_t (const hb_atomic_t &other) = delete; 244 245 void init (T* v_ = nullptr) { set_relaxed (v_); } 246 void set_relaxed (T* v_) { hb_atomic_ptr_impl_set_relaxed (&v, v_); } 247 T *get_relaxed () const { return (T *) hb_atomic_ptr_impl_get_relaxed (&v); } 248 T *get_acquire () const { return (T *) hb_atomic_ptr_impl_get ((void **) &v); } 249 bool cmpexch (T *old, T *new_) { return hb_atomic_ptr_impl_cmpexch ((void **) &v, (void *) old, (void *) new_); } 250 251 operator bool () const { return get_acquire () != nullptr; } 252 T * operator -> () const { return get_acquire (); } 253 template <typename C> operator C * () const { return get_acquire (); } 254 255 T *v = nullptr; 256 }; 257 258 #endif 259 260 static inline bool hb_barrier () 261 { 262 _hb_compiler_memory_r_barrier (); 263 return true; 264 } 265 266 267 #endif /* HB_ATOMIC_HH */