Andrew Top | 61a8495 | 2019-04-30 15:07:33 -0700 | [diff] [blame] | 1 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" |
| 2 | "http://www.w3.org/TR/html4/strict.dtd"> |
| 3 | <!-- Material used from: HTML 4.01 specs: http://www.w3.org/TR/html401/ --> |
| 4 | <html> |
| 5 | <head> |
| 6 | <META http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> |
| 7 | <title><atomic> design</title> |
| 8 | <link type="text/css" rel="stylesheet" href="menu.css"> |
| 9 | <link type="text/css" rel="stylesheet" href="content.css"> |
| 10 | </head> |
| 11 | |
| 12 | <body> |
| 13 | <div id="menu"> |
| 14 | <div> |
| 15 | <a href="https://llvm.org/">LLVM Home</a> |
| 16 | </div> |
| 17 | |
| 18 | <div class="submenu"> |
| 19 | <label>libc++ Info</label> |
| 20 | <a href="/index.html">About</a> |
| 21 | </div> |
| 22 | |
| 23 | <div class="submenu"> |
| 24 | <label>Quick Links</label> |
| 25 | <a href="https://lists.llvm.org/mailman/listinfo/cfe-dev">cfe-dev</a> |
| 26 | <a href="https://lists.llvm.org/mailman/listinfo/cfe-commits">cfe-commits</a> |
| 27 | <a href="https://bugs.llvm.org/">Bug Reports</a> |
| 28 | <a href="https://llvm.org/svn/llvm-project/libcxx/trunk/">Browse SVN</a> |
| 29 | <a href="https://llvm.org/viewvc/llvm-project/libcxx/trunk/">Browse ViewVC</a> |
| 30 | </div> |
| 31 | </div> |
| 32 | |
| 33 | <div id="content"> |
| 34 | <!--*********************************************************************--> |
| 35 | <h1><atomic> design</h1> |
| 36 | <!--*********************************************************************--> |
| 37 | |
| 38 | <p> |
| 39 | The compiler supplies all of the intrinsics as described below. This list of |
| 40 | intrinsics roughly parallels the requirements of the C and C++ atomics |
| 41 | proposals. The C and C++ library implementations simply drop through to these |
| 42 | intrinsics. Anything the platform does not support in hardware, the compiler |
| 43 | arranges for a (compiler-rt) library call to be made which will do the job with |
| 44 | a mutex, and in this case ignoring the memory ordering parameter (effectively |
| 45 | implementing <tt>memory_order_seq_cst</tt>). |
| 46 | </p> |
| 47 | |
| 48 | <p> |
| 49 | Ultimate efficiency is preferred over run time error checking. Undefined |
| 50 | behavior is acceptable when the inputs do not conform as defined below. |
| 51 | </p> |
| 52 | |
| 53 | <blockquote><pre> |
| 54 | <font color="#C80000">// In every intrinsic signature below, type* atomic_obj may be a pointer to a</font> |
| 55 | <font color="#C80000">// volatile-qualified type.</font> |
| 56 | <font color="#C80000">// Memory ordering values map to the following meanings:</font> |
| 57 | <font color="#C80000">// memory_order_relaxed == 0</font> |
| 58 | <font color="#C80000">// memory_order_consume == 1</font> |
| 59 | <font color="#C80000">// memory_order_acquire == 2</font> |
| 60 | <font color="#C80000">// memory_order_release == 3</font> |
| 61 | <font color="#C80000">// memory_order_acq_rel == 4</font> |
| 62 | <font color="#C80000">// memory_order_seq_cst == 5</font> |
| 63 | |
| 64 | <font color="#C80000">// type must be trivially copyable</font> |
| 65 | <font color="#C80000">// type represents a "type argument"</font> |
| 66 | bool __atomic_is_lock_free(type); |
| 67 | |
| 68 | <font color="#C80000">// type must be trivially copyable</font> |
| 69 | <font color="#C80000">// Behavior is defined for mem_ord = 0, 1, 2, 5</font> |
| 70 | type __atomic_load(const type* atomic_obj, int mem_ord); |
| 71 | |
| 72 | <font color="#C80000">// type must be trivially copyable</font> |
| 73 | <font color="#C80000">// Behavior is defined for mem_ord = 0, 3, 5</font> |
| 74 | void __atomic_store(type* atomic_obj, type desired, int mem_ord); |
| 75 | |
| 76 | <font color="#C80000">// type must be trivially copyable</font> |
| 77 | <font color="#C80000">// Behavior is defined for mem_ord = [0 ... 5]</font> |
| 78 | type __atomic_exchange(type* atomic_obj, type desired, int mem_ord); |
| 79 | |
| 80 | <font color="#C80000">// type must be trivially copyable</font> |
| 81 | <font color="#C80000">// Behavior is defined for mem_success = [0 ... 5],</font> |
| 82 | <font color="#C80000">// mem_failure <= mem_success</font> |
| 83 | <font color="#C80000">// mem_failure != 3</font> |
| 84 | <font color="#C80000">// mem_failure != 4</font> |
| 85 | bool __atomic_compare_exchange_strong(type* atomic_obj, |
| 86 | type* expected, type desired, |
| 87 | int mem_success, int mem_failure); |
| 88 | |
| 89 | <font color="#C80000">// type must be trivially copyable</font> |
| 90 | <font color="#C80000">// Behavior is defined for mem_success = [0 ... 5],</font> |
| 91 | <font color="#C80000">// mem_failure <= mem_success</font> |
| 92 | <font color="#C80000">// mem_failure != 3</font> |
| 93 | <font color="#C80000">// mem_failure != 4</font> |
| 94 | bool __atomic_compare_exchange_weak(type* atomic_obj, |
| 95 | type* expected, type desired, |
| 96 | int mem_success, int mem_failure); |
| 97 | |
| 98 | <font color="#C80000">// type is one of: char, signed char, unsigned char, short, unsigned short, int,</font> |
| 99 | <font color="#C80000">// unsigned int, long, unsigned long, long long, unsigned long long,</font> |
| 100 | <font color="#C80000">// char16_t, char32_t, wchar_t</font> |
| 101 | <font color="#C80000">// Behavior is defined for mem_ord = [0 ... 5]</font> |
| 102 | type __atomic_fetch_add(type* atomic_obj, type operand, int mem_ord); |
| 103 | |
| 104 | <font color="#C80000">// type is one of: char, signed char, unsigned char, short, unsigned short, int,</font> |
| 105 | <font color="#C80000">// unsigned int, long, unsigned long, long long, unsigned long long,</font> |
| 106 | <font color="#C80000">// char16_t, char32_t, wchar_t</font> |
| 107 | <font color="#C80000">// Behavior is defined for mem_ord = [0 ... 5]</font> |
| 108 | type __atomic_fetch_sub(type* atomic_obj, type operand, int mem_ord); |
| 109 | |
| 110 | <font color="#C80000">// type is one of: char, signed char, unsigned char, short, unsigned short, int,</font> |
| 111 | <font color="#C80000">// unsigned int, long, unsigned long, long long, unsigned long long,</font> |
| 112 | <font color="#C80000">// char16_t, char32_t, wchar_t</font> |
| 113 | <font color="#C80000">// Behavior is defined for mem_ord = [0 ... 5]</font> |
| 114 | type __atomic_fetch_and(type* atomic_obj, type operand, int mem_ord); |
| 115 | |
| 116 | <font color="#C80000">// type is one of: char, signed char, unsigned char, short, unsigned short, int,</font> |
| 117 | <font color="#C80000">// unsigned int, long, unsigned long, long long, unsigned long long,</font> |
| 118 | <font color="#C80000">// char16_t, char32_t, wchar_t</font> |
| 119 | <font color="#C80000">// Behavior is defined for mem_ord = [0 ... 5]</font> |
| 120 | type __atomic_fetch_or(type* atomic_obj, type operand, int mem_ord); |
| 121 | |
| 122 | <font color="#C80000">// type is one of: char, signed char, unsigned char, short, unsigned short, int,</font> |
| 123 | <font color="#C80000">// unsigned int, long, unsigned long, long long, unsigned long long,</font> |
| 124 | <font color="#C80000">// char16_t, char32_t, wchar_t</font> |
| 125 | <font color="#C80000">// Behavior is defined for mem_ord = [0 ... 5]</font> |
| 126 | type __atomic_fetch_xor(type* atomic_obj, type operand, int mem_ord); |
| 127 | |
| 128 | <font color="#C80000">// Behavior is defined for mem_ord = [0 ... 5]</font> |
| 129 | void* __atomic_fetch_add(void** atomic_obj, ptrdiff_t operand, int mem_ord); |
| 130 | void* __atomic_fetch_sub(void** atomic_obj, ptrdiff_t operand, int mem_ord); |
| 131 | |
| 132 | <font color="#C80000">// Behavior is defined for mem_ord = [0 ... 5]</font> |
| 133 | void __atomic_thread_fence(int mem_ord); |
| 134 | void __atomic_signal_fence(int mem_ord); |
| 135 | </pre></blockquote> |
| 136 | |
| 137 | <p> |
| 138 | If desired the intrinsics taking a single <tt>mem_ord</tt> parameter can default |
| 139 | this argument to 5. |
| 140 | </p> |
| 141 | |
| 142 | <p> |
| 143 | If desired the intrinsics taking two ordering parameters can default |
| 144 | <tt>mem_success</tt> to 5, and <tt>mem_failure</tt> to |
| 145 | <tt>translate_memory_order(mem_success)</tt> where |
| 146 | <tt>translate_memory_order(mem_success)</tt> is defined as: |
| 147 | </p> |
| 148 | |
| 149 | <blockquote><pre> |
| 150 | int |
| 151 | translate_memory_order(int o) |
| 152 | { |
| 153 | switch (o) |
| 154 | { |
| 155 | case 4: |
| 156 | return 2; |
| 157 | case 3: |
| 158 | return 0; |
| 159 | } |
| 160 | return o; |
| 161 | } |
| 162 | </pre></blockquote> |
| 163 | |
| 164 | <p> |
| 165 | Below are representative C++ implementations of all of the operations. Their |
| 166 | purpose is to document the desired semantics of each operation, assuming |
| 167 | <tt>memory_order_seq_cst</tt>. This is essentially the code that will be called |
| 168 | if the front end calls out to compiler-rt. |
| 169 | </p> |
| 170 | |
| 171 | <blockquote><pre> |
| 172 | template <class T> |
| 173 | T |
| 174 | __atomic_load(T const volatile* obj) |
| 175 | { |
| 176 | unique_lock<mutex> _(some_mutex); |
| 177 | return *obj; |
| 178 | } |
| 179 | |
| 180 | template <class T> |
| 181 | void |
| 182 | __atomic_store(T volatile* obj, T desr) |
| 183 | { |
| 184 | unique_lock<mutex> _(some_mutex); |
| 185 | *obj = desr; |
| 186 | } |
| 187 | |
| 188 | template <class T> |
| 189 | T |
| 190 | __atomic_exchange(T volatile* obj, T desr) |
| 191 | { |
| 192 | unique_lock<mutex> _(some_mutex); |
| 193 | T r = *obj; |
| 194 | *obj = desr; |
| 195 | return r; |
| 196 | } |
| 197 | |
| 198 | template <class T> |
| 199 | bool |
| 200 | __atomic_compare_exchange_strong(T volatile* obj, T* exp, T desr) |
| 201 | { |
| 202 | unique_lock<mutex> _(some_mutex); |
| 203 | if (std::memcmp(const_cast<T*>(obj), exp, sizeof(T)) == 0) <font color="#C80000">// if (*obj == *exp)</font> |
| 204 | { |
| 205 | std::memcpy(const_cast<T*>(obj), &desr, sizeof(T)); <font color="#C80000">// *obj = desr;</font> |
| 206 | return true; |
| 207 | } |
| 208 | std::memcpy(exp, const_cast<T*>(obj), sizeof(T)); <font color="#C80000">// *exp = *obj;</font> |
| 209 | return false; |
| 210 | } |
| 211 | |
| 212 | <font color="#C80000">// May spuriously return false (even if *obj == *exp)</font> |
| 213 | template <class T> |
| 214 | bool |
| 215 | __atomic_compare_exchange_weak(T volatile* obj, T* exp, T desr) |
| 216 | { |
| 217 | unique_lock<mutex> _(some_mutex); |
| 218 | if (std::memcmp(const_cast<T*>(obj), exp, sizeof(T)) == 0) <font color="#C80000">// if (*obj == *exp)</font> |
| 219 | { |
| 220 | std::memcpy(const_cast<T*>(obj), &desr, sizeof(T)); <font color="#C80000">// *obj = desr;</font> |
| 221 | return true; |
| 222 | } |
| 223 | std::memcpy(exp, const_cast<T*>(obj), sizeof(T)); <font color="#C80000">// *exp = *obj;</font> |
| 224 | return false; |
| 225 | } |
| 226 | |
| 227 | template <class T> |
| 228 | T |
| 229 | __atomic_fetch_add(T volatile* obj, T operand) |
| 230 | { |
| 231 | unique_lock<mutex> _(some_mutex); |
| 232 | T r = *obj; |
| 233 | *obj += operand; |
| 234 | return r; |
| 235 | } |
| 236 | |
| 237 | template <class T> |
| 238 | T |
| 239 | __atomic_fetch_sub(T volatile* obj, T operand) |
| 240 | { |
| 241 | unique_lock<mutex> _(some_mutex); |
| 242 | T r = *obj; |
| 243 | *obj -= operand; |
| 244 | return r; |
| 245 | } |
| 246 | |
| 247 | template <class T> |
| 248 | T |
| 249 | __atomic_fetch_and(T volatile* obj, T operand) |
| 250 | { |
| 251 | unique_lock<mutex> _(some_mutex); |
| 252 | T r = *obj; |
| 253 | *obj &= operand; |
| 254 | return r; |
| 255 | } |
| 256 | |
| 257 | template <class T> |
| 258 | T |
| 259 | __atomic_fetch_or(T volatile* obj, T operand) |
| 260 | { |
| 261 | unique_lock<mutex> _(some_mutex); |
| 262 | T r = *obj; |
| 263 | *obj |= operand; |
| 264 | return r; |
| 265 | } |
| 266 | |
| 267 | template <class T> |
| 268 | T |
| 269 | __atomic_fetch_xor(T volatile* obj, T operand) |
| 270 | { |
| 271 | unique_lock<mutex> _(some_mutex); |
| 272 | T r = *obj; |
| 273 | *obj ^= operand; |
| 274 | return r; |
| 275 | } |
| 276 | |
| 277 | void* |
| 278 | __atomic_fetch_add(void* volatile* obj, ptrdiff_t operand) |
| 279 | { |
| 280 | unique_lock<mutex> _(some_mutex); |
| 281 | void* r = *obj; |
| 282 | (char*&)(*obj) += operand; |
| 283 | return r; |
| 284 | } |
| 285 | |
| 286 | void* |
| 287 | __atomic_fetch_sub(void* volatile* obj, ptrdiff_t operand) |
| 288 | { |
| 289 | unique_lock<mutex> _(some_mutex); |
| 290 | void* r = *obj; |
| 291 | (char*&)(*obj) -= operand; |
| 292 | return r; |
| 293 | } |
| 294 | |
| 295 | void __atomic_thread_fence() |
| 296 | { |
| 297 | unique_lock<mutex> _(some_mutex); |
| 298 | } |
| 299 | |
| 300 | void __atomic_signal_fence() |
| 301 | { |
| 302 | unique_lock<mutex> _(some_mutex); |
| 303 | } |
| 304 | </pre></blockquote> |
| 305 | |
| 306 | |
| 307 | </div> |
| 308 | </body> |
| 309 | </html> |