mfbt/Atomics.h

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 /*
michael@0 8 * Implements (almost always) lock-free atomic operations. The operations here
michael@0 9 * are a subset of that which can be found in C++11's <atomic> header, with a
michael@0 10 * different API to enforce consistent memory ordering constraints.
michael@0 11 *
michael@0 12 * Anyone caught using |volatile| for inter-thread memory safety needs to be
michael@0 13 * sent a copy of this header and the C++11 standard.
michael@0 14 */
michael@0 15
michael@0 16 #ifndef mozilla_Atomics_h
michael@0 17 #define mozilla_Atomics_h
michael@0 18
michael@0 19 #include "mozilla/Assertions.h"
michael@0 20 #include "mozilla/Attributes.h"
michael@0 21 #include "mozilla/Compiler.h"
michael@0 22 #include "mozilla/TypeTraits.h"
michael@0 23
michael@0 24 #include <stdint.h>
michael@0 25
michael@0 26 /*
michael@0 27 * Our minimum deployment target on clang/OS X is OS X 10.6, whose SDK
michael@0 28 * does not have <atomic>. So be sure to check for <atomic> support
michael@0 29 * along with C++0x support.
michael@0 30 */
michael@0 31 #if defined(__clang__) || defined(__GNUC__)
michael@0 32 /*
michael@0 33 * Clang doesn't like <atomic> from libstdc++ before 4.7 due to the
michael@0 34 * loose typing of the atomic builtins. GCC 4.5 and 4.6 lacks inline
michael@0 35 * definitions for unspecialized std::atomic and causes linking errors.
michael@0 36 * Therefore, we require at least 4.7.0 for using libstdc++.
michael@0 37 */
michael@0 38 # if MOZ_USING_LIBSTDCXX && MOZ_LIBSTDCXX_VERSION_AT_LEAST(4, 7, 0)
michael@0 39 # define MOZ_HAVE_CXX11_ATOMICS
michael@0 40 # elif MOZ_USING_LIBCXX
michael@0 41 # define MOZ_HAVE_CXX11_ATOMICS
michael@0 42 # endif
michael@0 43 #elif defined(_MSC_VER) && _MSC_VER >= 1700
michael@0 44 # if defined(DEBUG)
michael@0 45 /*
michael@0 46 * Provide our own failure code since we're having trouble linking to
michael@0 47 * std::_Debug_message (bug 982310).
michael@0 48 */
michael@0 49 # define _INVALID_MEMORY_ORDER MOZ_CRASH("Invalid memory order")
michael@0 50 # endif
michael@0 51 # define MOZ_HAVE_CXX11_ATOMICS
michael@0 52 #endif
michael@0 53
michael@0 54 namespace mozilla {
michael@0 55
michael@0 56 /**
michael@0 57 * An enum of memory ordering possibilities for atomics.
michael@0 58 *
michael@0 59 * Memory ordering is the observable state of distinct values in memory.
michael@0 60 * (It's a separate concept from atomicity, which concerns whether an
michael@0 61 * operation can ever be observed in an intermediate state. Don't
michael@0 62 * conflate the two!) Given a sequence of operations in source code on
michael@0 63 * memory, it is *not* always the case that, at all times and on all
michael@0 64 * cores, those operations will appear to have occurred in that exact
michael@0 65 * sequence. First, the compiler might reorder that sequence, if it
michael@0 66 * thinks another ordering will be more efficient. Second, the CPU may
michael@0 67 * not expose so consistent a view of memory. CPUs will often perform
michael@0 68 * their own instruction reordering, above and beyond that performed by
michael@0 69 * the compiler. And each core has its own memory caches, and accesses
michael@0 70 * (reads and writes both) to "memory" may only resolve to out-of-date
michael@0 71 * cache entries -- not to the "most recently" performed operation in
michael@0 72 * some global sense. Any access to a value that may be used by
michael@0 73 * multiple threads, potentially across multiple cores, must therefore
michael@0 74 * have a memory ordering imposed on it, for all code on all
michael@0 75 * threads/cores to have a sufficiently coherent worldview.
michael@0 76 *
michael@0 77 * http://gcc.gnu.org/wiki/Atomic/GCCMM/AtomicSync and
michael@0 78 * http://en.cppreference.com/w/cpp/atomic/memory_order go into more
michael@0 79 * detail on all this, including examples of how each mode works.
michael@0 80 *
michael@0 81 * Note that for simplicity and practicality, not all of the modes in
michael@0 82 * C++11 are supported. The missing C++11 modes are either subsumed by
michael@0 83 * the modes we provide below, or not relevant for the CPUs we support
michael@0 84 * in Gecko. These three modes are confusing enough as it is!
michael@0 85 */
michael@0 86 enum MemoryOrdering {
michael@0 87 /*
michael@0 88 * Relaxed ordering is the simplest memory ordering: none at all.
michael@0 89 * When the result of a write is observed, nothing may be inferred
michael@0 90 * about other memory. Writes ostensibly performed "before" on the
michael@0 91 * writing thread may not yet be visible. Writes performed "after" on
michael@0 92 * the writing thread may already be visible, if the compiler or CPU
michael@0 93 * reordered them. (The latter can happen if reads and/or writes get
michael@0 94 * held up in per-processor caches.) Relaxed ordering means
michael@0 95 * operations can always use cached values (as long as the actual
michael@0 96 * updates to atomic values actually occur, correctly, eventually), so
michael@0 97 * it's usually the fastest sort of atomic access. For this reason,
michael@0 98 * *it's also the most dangerous kind of access*.
michael@0 99 *
michael@0 100 * Relaxed ordering is good for things like process-wide statistics
michael@0 101 * counters that don't need to be consistent with anything else, so
michael@0 102 * long as updates themselves are atomic. (And so long as any
michael@0 103 * observations of that value can tolerate being out-of-date -- if you
michael@0 104 * need some sort of up-to-date value, you need some sort of other
michael@0 105 * synchronizing operation.) It's *not* good for locks, mutexes,
michael@0 106 * reference counts, etc. that mediate access to other memory, or must
michael@0 107 * be observably consistent with other memory.
michael@0 108 *
michael@0 109 * x86 architectures don't take advantage of the optimization
michael@0 110 * opportunities that relaxed ordering permits. Thus it's possible
michael@0 111 * that using relaxed ordering will "work" on x86 but fail elsewhere
michael@0 112 * (ARM, say, which *does* implement non-sequentially-consistent
michael@0 113 * relaxed ordering semantics). Be extra-careful using relaxed
michael@0 114 * ordering if you can't easily test non-x86 architectures!
michael@0 115 */
michael@0 116 Relaxed,
michael@0 117 /*
michael@0 118 * When an atomic value is updated with ReleaseAcquire ordering, and
michael@0 119 * that new value is observed with ReleaseAcquire ordering, prior
michael@0 120 * writes (atomic or not) are also observable. What ReleaseAcquire
michael@0 121 * *doesn't* give you is any observable ordering guarantees for
michael@0 122 * ReleaseAcquire-ordered operations on different objects. For
michael@0 123 * example, if there are two cores that each perform ReleaseAcquire
michael@0 124 * operations on separate objects, each core may or may not observe
michael@0 125 * the operations made by the other core. The only way the cores can
michael@0 126 * be synchronized with ReleaseAcquire is if they both
michael@0 127 * ReleaseAcquire-access the same object. This implies that you can't
michael@0 128 * necessarily describe some global total ordering of ReleaseAcquire
michael@0 129 * operations.
michael@0 130 *
michael@0 131 * ReleaseAcquire ordering is good for (as the name implies) atomic
michael@0 132 * operations on values controlling ownership of things: reference
michael@0 133 * counts, mutexes, and the like. However, if you are thinking about
michael@0 134 * using these to implement your own locks or mutexes, you should take
michael@0 135 * a good, hard look at actual lock or mutex primitives first.
michael@0 136 */
michael@0 137 ReleaseAcquire,
michael@0 138 /*
michael@0 139 * When an atomic value is updated with SequentiallyConsistent
michael@0 140 * ordering, all writes observable when the update is observed, just
michael@0 141 * as with ReleaseAcquire ordering. But, furthermore, a global total
michael@0 142 * ordering of SequentiallyConsistent operations *can* be described.
michael@0 143 * For example, if two cores perform SequentiallyConsistent operations
michael@0 144 * on separate objects, one core will observably perform its update
michael@0 145 * (and all previous operations will have completed), then the other
michael@0 146 * core will observably perform its update (and all previous
michael@0 147 * operations will have completed). (Although those previous
michael@0 148 * operations aren't themselves ordered -- they could be intermixed,
michael@0 149 * or ordered if they occur on atomic values with ordering
michael@0 150 * requirements.) SequentiallyConsistent is the *simplest and safest*
michael@0 151 * ordering of atomic operations -- it's always as if one operation
michael@0 152 * happens, then another, then another, in some order -- and every
michael@0 153 * core observes updates to happen in that single order. Because it
michael@0 154 * has the most synchronization requirements, operations ordered this
michael@0 155 * way also tend to be slowest.
michael@0 156 *
michael@0 157 * SequentiallyConsistent ordering can be desirable when multiple
michael@0 158 * threads observe objects, and they all have to agree on the
michael@0 159 * observable order of changes to them. People expect
michael@0 160 * SequentiallyConsistent ordering, even if they shouldn't, when
michael@0 161 * writing code, atomic or otherwise. SequentiallyConsistent is also
michael@0 162 * the ordering of choice when designing lockless data structures. If
michael@0 163 * you don't know what order to use, use this one.
michael@0 164 */
michael@0 165 SequentiallyConsistent,
michael@0 166 };
michael@0 167
michael@0 168 } // namespace mozilla
michael@0 169
michael@0 170 // Build up the underlying intrinsics.
michael@0 171 #ifdef MOZ_HAVE_CXX11_ATOMICS
michael@0 172
michael@0 173 # include <atomic>
michael@0 174
michael@0 175 namespace mozilla {
michael@0 176 namespace detail {
michael@0 177
michael@0 178 /*
michael@0 179 * We provide CompareExchangeFailureOrder to work around a bug in some
michael@0 180 * versions of GCC's <atomic> header. See bug 898491.
michael@0 181 */
michael@0 182 template<MemoryOrdering Order> struct AtomicOrderConstraints;
michael@0 183
michael@0 184 template<>
michael@0 185 struct AtomicOrderConstraints<Relaxed>
michael@0 186 {
michael@0 187 static const std::memory_order AtomicRMWOrder = std::memory_order_relaxed;
michael@0 188 static const std::memory_order LoadOrder = std::memory_order_relaxed;
michael@0 189 static const std::memory_order StoreOrder = std::memory_order_relaxed;
michael@0 190 static const std::memory_order CompareExchangeFailureOrder =
michael@0 191 std::memory_order_relaxed;
michael@0 192 };
michael@0 193
michael@0 194 template<>
michael@0 195 struct AtomicOrderConstraints<ReleaseAcquire>
michael@0 196 {
michael@0 197 static const std::memory_order AtomicRMWOrder = std::memory_order_acq_rel;
michael@0 198 static const std::memory_order LoadOrder = std::memory_order_acquire;
michael@0 199 static const std::memory_order StoreOrder = std::memory_order_release;
michael@0 200 static const std::memory_order CompareExchangeFailureOrder =
michael@0 201 std::memory_order_acquire;
michael@0 202 };
michael@0 203
michael@0 204 template<>
michael@0 205 struct AtomicOrderConstraints<SequentiallyConsistent>
michael@0 206 {
michael@0 207 static const std::memory_order AtomicRMWOrder = std::memory_order_seq_cst;
michael@0 208 static const std::memory_order LoadOrder = std::memory_order_seq_cst;
michael@0 209 static const std::memory_order StoreOrder = std::memory_order_seq_cst;
michael@0 210 static const std::memory_order CompareExchangeFailureOrder =
michael@0 211 std::memory_order_seq_cst;
michael@0 212 };
michael@0 213
michael@0 214 template<typename T, MemoryOrdering Order>
michael@0 215 struct IntrinsicBase
michael@0 216 {
michael@0 217 typedef std::atomic<T> ValueType;
michael@0 218 typedef AtomicOrderConstraints<Order> OrderedOp;
michael@0 219 };
michael@0 220
michael@0 221 template<typename T, MemoryOrdering Order>
michael@0 222 struct IntrinsicMemoryOps : public IntrinsicBase<T, Order>
michael@0 223 {
michael@0 224 typedef IntrinsicBase<T, Order> Base;
michael@0 225 static T load(const typename Base::ValueType& ptr) {
michael@0 226 return ptr.load(Base::OrderedOp::LoadOrder);
michael@0 227 }
michael@0 228 static void store(typename Base::ValueType& ptr, T val) {
michael@0 229 ptr.store(val, Base::OrderedOp::StoreOrder);
michael@0 230 }
michael@0 231 static T exchange(typename Base::ValueType& ptr, T val) {
michael@0 232 return ptr.exchange(val, Base::OrderedOp::AtomicRMWOrder);
michael@0 233 }
michael@0 234 static bool compareExchange(typename Base::ValueType& ptr, T oldVal, T newVal) {
michael@0 235 return ptr.compare_exchange_strong(oldVal, newVal,
michael@0 236 Base::OrderedOp::AtomicRMWOrder,
michael@0 237 Base::OrderedOp::CompareExchangeFailureOrder);
michael@0 238 }
michael@0 239 };
michael@0 240
michael@0 241 template<typename T, MemoryOrdering Order>
michael@0 242 struct IntrinsicAddSub : public IntrinsicBase<T, Order>
michael@0 243 {
michael@0 244 typedef IntrinsicBase<T, Order> Base;
michael@0 245 static T add(typename Base::ValueType& ptr, T val) {
michael@0 246 return ptr.fetch_add(val, Base::OrderedOp::AtomicRMWOrder);
michael@0 247 }
michael@0 248 static T sub(typename Base::ValueType& ptr, T val) {
michael@0 249 return ptr.fetch_sub(val, Base::OrderedOp::AtomicRMWOrder);
michael@0 250 }
michael@0 251 };
michael@0 252
michael@0 253 template<typename T, MemoryOrdering Order>
michael@0 254 struct IntrinsicAddSub<T*, Order> : public IntrinsicBase<T*, Order>
michael@0 255 {
michael@0 256 typedef IntrinsicBase<T*, Order> Base;
michael@0 257 static T* add(typename Base::ValueType& ptr, ptrdiff_t val) {
michael@0 258 return ptr.fetch_add(fixupAddend(val), Base::OrderedOp::AtomicRMWOrder);
michael@0 259 }
michael@0 260 static T* sub(typename Base::ValueType& ptr, ptrdiff_t val) {
michael@0 261 return ptr.fetch_sub(fixupAddend(val), Base::OrderedOp::AtomicRMWOrder);
michael@0 262 }
michael@0 263 private:
michael@0 264 /*
michael@0 265 * GCC 4.6's <atomic> header has a bug where adding X to an
michael@0 266 * atomic<T*> is not the same as adding X to a T*. Hence the need
michael@0 267 * for this function to provide the correct addend.
michael@0 268 */
michael@0 269 static ptrdiff_t fixupAddend(ptrdiff_t val) {
michael@0 270 #if defined(__clang__) || defined(_MSC_VER)
michael@0 271 return val;
michael@0 272 #elif defined(__GNUC__) && MOZ_GCC_VERSION_AT_LEAST(4, 6, 0) && \
michael@0 273 !MOZ_GCC_VERSION_AT_LEAST(4, 7, 0)
michael@0 274 return val * sizeof(T);
michael@0 275 #else
michael@0 276 return val;
michael@0 277 #endif
michael@0 278 }
michael@0 279 };
michael@0 280
michael@0 281 template<typename T, MemoryOrdering Order>
michael@0 282 struct IntrinsicIncDec : public IntrinsicAddSub<T, Order>
michael@0 283 {
michael@0 284 typedef IntrinsicBase<T, Order> Base;
michael@0 285 static T inc(typename Base::ValueType& ptr) {
michael@0 286 return IntrinsicAddSub<T, Order>::add(ptr, 1);
michael@0 287 }
michael@0 288 static T dec(typename Base::ValueType& ptr) {
michael@0 289 return IntrinsicAddSub<T, Order>::sub(ptr, 1);
michael@0 290 }
michael@0 291 };
michael@0 292
michael@0 293 template<typename T, MemoryOrdering Order>
michael@0 294 struct AtomicIntrinsics : public IntrinsicMemoryOps<T, Order>,
michael@0 295 public IntrinsicIncDec<T, Order>
michael@0 296 {
michael@0 297 typedef IntrinsicBase<T, Order> Base;
michael@0 298 static T or_(typename Base::ValueType& ptr, T val) {
michael@0 299 return ptr.fetch_or(val, Base::OrderedOp::AtomicRMWOrder);
michael@0 300 }
michael@0 301 static T xor_(typename Base::ValueType& ptr, T val) {
michael@0 302 return ptr.fetch_xor(val, Base::OrderedOp::AtomicRMWOrder);
michael@0 303 }
michael@0 304 static T and_(typename Base::ValueType& ptr, T val) {
michael@0 305 return ptr.fetch_and(val, Base::OrderedOp::AtomicRMWOrder);
michael@0 306 }
michael@0 307 };
michael@0 308
michael@0 309 template<typename T, MemoryOrdering Order>
michael@0 310 struct AtomicIntrinsics<T*, Order>
michael@0 311 : public IntrinsicMemoryOps<T*, Order>, public IntrinsicIncDec<T*, Order>
michael@0 312 {
michael@0 313 };
michael@0 314
michael@0 315 } // namespace detail
michael@0 316 } // namespace mozilla
michael@0 317
michael@0 318 #elif defined(__GNUC__)
michael@0 319
michael@0 320 namespace mozilla {
michael@0 321 namespace detail {
michael@0 322
michael@0 323 /*
michael@0 324 * The __sync_* family of intrinsics is documented here:
michael@0 325 *
michael@0 326 * http://gcc.gnu.org/onlinedocs/gcc-4.6.4/gcc/Atomic-Builtins.html
michael@0 327 *
michael@0 328 * While these intrinsics are deprecated in favor of the newer __atomic_*
michael@0 329 * family of intrincs:
michael@0 330 *
michael@0 331 * http://gcc.gnu.org/onlinedocs/gcc-4.7.3/gcc/_005f_005fatomic-Builtins.html
michael@0 332 *
michael@0 333 * any GCC version that supports the __atomic_* intrinsics will also support
michael@0 334 * the <atomic> header and so will be handled above. We provide a version of
michael@0 335 * atomics using the __sync_* intrinsics to support older versions of GCC.
michael@0 336 *
michael@0 337 * All __sync_* intrinsics that we use below act as full memory barriers, for
michael@0 338 * both compiler and hardware reordering, except for __sync_lock_test_and_set,
michael@0 339 * which is a only an acquire barrier. When we call __sync_lock_test_and_set,
michael@0 340 * we add a barrier above it as appropriate.
michael@0 341 */
michael@0 342
michael@0 343 template<MemoryOrdering Order> struct Barrier;
michael@0 344
michael@0 345 /*
michael@0 346 * Some processors (in particular, x86) don't require quite so many calls to
michael@0 347 * __sync_sychronize as our specializations of Barrier produce. If
michael@0 348 * performance turns out to be an issue, defining these specializations
michael@0 349 * on a per-processor basis would be a good first tuning step.
michael@0 350 */
michael@0 351
michael@0 352 template<>
michael@0 353 struct Barrier<Relaxed>
michael@0 354 {
michael@0 355 static void beforeLoad() {}
michael@0 356 static void afterLoad() {}
michael@0 357 static void beforeStore() {}
michael@0 358 static void afterStore() {}
michael@0 359 };
michael@0 360
michael@0 361 template<>
michael@0 362 struct Barrier<ReleaseAcquire>
michael@0 363 {
michael@0 364 static void beforeLoad() {}
michael@0 365 static void afterLoad() { __sync_synchronize(); }
michael@0 366 static void beforeStore() { __sync_synchronize(); }
michael@0 367 static void afterStore() {}
michael@0 368 };
michael@0 369
michael@0 370 template<>
michael@0 371 struct Barrier<SequentiallyConsistent>
michael@0 372 {
michael@0 373 static void beforeLoad() { __sync_synchronize(); }
michael@0 374 static void afterLoad() { __sync_synchronize(); }
michael@0 375 static void beforeStore() { __sync_synchronize(); }
michael@0 376 static void afterStore() { __sync_synchronize(); }
michael@0 377 };
michael@0 378
michael@0 379 template<typename T, MemoryOrdering Order>
michael@0 380 struct IntrinsicMemoryOps
michael@0 381 {
michael@0 382 static T load(const T& ptr) {
michael@0 383 Barrier<Order>::beforeLoad();
michael@0 384 T val = ptr;
michael@0 385 Barrier<Order>::afterLoad();
michael@0 386 return val;
michael@0 387 }
michael@0 388 static void store(T& ptr, T val) {
michael@0 389 Barrier<Order>::beforeStore();
michael@0 390 ptr = val;
michael@0 391 Barrier<Order>::afterStore();
michael@0 392 }
michael@0 393 static T exchange(T& ptr, T val) {
michael@0 394 // __sync_lock_test_and_set is only an acquire barrier; loads and stores
michael@0 395 // can't be moved up from after to before it, but they can be moved down
michael@0 396 // from before to after it. We may want a stricter ordering, so we need
michael@0 397 // an explicit barrier.
michael@0 398
michael@0 399 Barrier<Order>::beforeStore();
michael@0 400 return __sync_lock_test_and_set(&ptr, val);
michael@0 401 }
michael@0 402 static bool compareExchange(T& ptr, T oldVal, T newVal) {
michael@0 403 return __sync_bool_compare_and_swap(&ptr, oldVal, newVal);
michael@0 404 }
michael@0 405 };
michael@0 406
michael@0 407 template<typename T>
michael@0 408 struct IntrinsicAddSub
michael@0 409 {
michael@0 410 typedef T ValueType;
michael@0 411 static T add(T& ptr, T val) {
michael@0 412 return __sync_fetch_and_add(&ptr, val);
michael@0 413 }
michael@0 414 static T sub(T& ptr, T val) {
michael@0 415 return __sync_fetch_and_sub(&ptr, val);
michael@0 416 }
michael@0 417 };
michael@0 418
michael@0 419 template<typename T>
michael@0 420 struct IntrinsicAddSub<T*>
michael@0 421 {
michael@0 422 typedef T* ValueType;
michael@0 423 /*
michael@0 424 * The reinterpret_casts are needed so that
michael@0 425 * __sync_fetch_and_{add,sub} will properly type-check.
michael@0 426 *
michael@0 427 * Also, these functions do not provide standard semantics for
michael@0 428 * pointer types, so we need to adjust the addend.
michael@0 429 */
michael@0 430 static ValueType add(ValueType& ptr, ptrdiff_t val) {
michael@0 431 ValueType amount = reinterpret_cast<ValueType>(val * sizeof(T));
michael@0 432 return __sync_fetch_and_add(&ptr, amount);
michael@0 433 }
michael@0 434 static ValueType sub(ValueType& ptr, ptrdiff_t val) {
michael@0 435 ValueType amount = reinterpret_cast<ValueType>(val * sizeof(T));
michael@0 436 return __sync_fetch_and_sub(&ptr, amount);
michael@0 437 }
michael@0 438 };
michael@0 439
michael@0 440 template<typename T>
michael@0 441 struct IntrinsicIncDec : public IntrinsicAddSub<T>
michael@0 442 {
michael@0 443 static T inc(T& ptr) { return IntrinsicAddSub<T>::add(ptr, 1); }
michael@0 444 static T dec(T& ptr) { return IntrinsicAddSub<T>::sub(ptr, 1); }
michael@0 445 };
michael@0 446
michael@0 447 template<typename T, MemoryOrdering Order>
michael@0 448 struct AtomicIntrinsics : public IntrinsicMemoryOps<T, Order>,
michael@0 449 public IntrinsicIncDec<T>
michael@0 450 {
michael@0 451 static T or_(T& ptr, T val) {
michael@0 452 return __sync_fetch_and_or(&ptr, val);
michael@0 453 }
michael@0 454 static T xor_(T& ptr, T val) {
michael@0 455 return __sync_fetch_and_xor(&ptr, val);
michael@0 456 }
michael@0 457 static T and_(T& ptr, T val) {
michael@0 458 return __sync_fetch_and_and(&ptr, val);
michael@0 459 }
michael@0 460 };
michael@0 461
michael@0 462 template<typename T, MemoryOrdering Order>
michael@0 463 struct AtomicIntrinsics<T*, Order> : public IntrinsicMemoryOps<T*, Order>,
michael@0 464 public IntrinsicIncDec<T*>
michael@0 465 {
michael@0 466 };
michael@0 467
michael@0 468 } // namespace detail
michael@0 469 } // namespace mozilla
michael@0 470
michael@0 471 #elif defined(_MSC_VER)
michael@0 472
michael@0 473 /*
michael@0 474 * Windows comes with a full complement of atomic operations.
michael@0 475 * Unfortunately, most of those aren't available for Windows XP (even if
michael@0 476 * the compiler supports intrinsics for them), which is the oldest
michael@0 477 * version of Windows we support. Therefore, we only provide operations
michael@0 478 * on 32-bit datatypes for 32-bit Windows versions; for 64-bit Windows
michael@0 479 * versions, we support 64-bit datatypes as well.
michael@0 480 *
michael@0 481 * To avoid namespace pollution issues, we declare whatever functions we
michael@0 482 * need ourselves.
michael@0 483 */
michael@0 484
michael@0 485 extern "C" {
michael@0 486 long __cdecl _InterlockedExchangeAdd(long volatile* dst, long value);
michael@0 487 long __cdecl _InterlockedOr(long volatile* dst, long value);
michael@0 488 long __cdecl _InterlockedXor(long volatile* dst, long value);
michael@0 489 long __cdecl _InterlockedAnd(long volatile* dst, long value);
michael@0 490 long __cdecl _InterlockedExchange(long volatile *dst, long value);
michael@0 491 long __cdecl _InterlockedCompareExchange(long volatile *dst, long newVal, long oldVal);
michael@0 492 }
michael@0 493
michael@0 494 # pragma intrinsic(_InterlockedExchangeAdd)
michael@0 495 # pragma intrinsic(_InterlockedOr)
michael@0 496 # pragma intrinsic(_InterlockedXor)
michael@0 497 # pragma intrinsic(_InterlockedAnd)
michael@0 498 # pragma intrinsic(_InterlockedExchange)
michael@0 499 # pragma intrinsic(_InterlockedCompareExchange)
michael@0 500
michael@0 501 namespace mozilla {
michael@0 502 namespace detail {
michael@0 503
michael@0 504 # if !defined(_M_IX86) && !defined(_M_X64)
michael@0 505 /*
michael@0 506 * The implementations below are optimized for x86ish systems. You
michael@0 507 * will have to modify them if you are porting to Windows on a
michael@0 508 * different architecture.
michael@0 509 */
michael@0 510 # error "Unknown CPU type"
michael@0 511 # endif
michael@0 512
michael@0 513 /*
michael@0 514 * The PrimitiveIntrinsics template should define |Type|, the datatype of size
michael@0 515 * DataSize upon which we operate, and the following eight functions.
michael@0 516 *
michael@0 517 * static Type add(Type* ptr, Type val);
michael@0 518 * static Type sub(Type* ptr, Type val);
michael@0 519 * static Type or_(Type* ptr, Type val);
michael@0 520 * static Type xor_(Type* ptr, Type val);
michael@0 521 * static Type and_(Type* ptr, Type val);
michael@0 522 *
michael@0 523 * These functions perform the obvious operation on the value contained in
michael@0 524 * |*ptr| combined with |val| and return the value previously stored in
michael@0 525 * |*ptr|.
michael@0 526 *
michael@0 527 * static void store(Type* ptr, Type val);
michael@0 528 *
michael@0 529 * This function atomically stores |val| into |*ptr| and must provide a full
michael@0 530 * memory fence after the store to prevent compiler and hardware instruction
michael@0 531 * reordering. It should also act as a compiler barrier to prevent reads and
michael@0 532 * writes from moving to after the store.
michael@0 533 *
michael@0 534 * static Type exchange(Type* ptr, Type val);
michael@0 535 *
michael@0 536 * This function atomically stores |val| into |*ptr| and returns the previous
michael@0 537 * contents of *ptr;
michael@0 538 *
michael@0 539 * static bool compareExchange(Type* ptr, Type oldVal, Type newVal);
michael@0 540 *
michael@0 541 * This function atomically performs the following operation:
michael@0 542 *
michael@0 543 * if (*ptr == oldVal) {
michael@0 544 * *ptr = newVal;
michael@0 545 * return true;
michael@0 546 * } else {
michael@0 547 * return false;
michael@0 548 * }
michael@0 549 *
michael@0 550 */
michael@0 551 template<size_t DataSize> struct PrimitiveIntrinsics;
michael@0 552
michael@0 553 template<>
michael@0 554 struct PrimitiveIntrinsics<4>
michael@0 555 {
michael@0 556 typedef long Type;
michael@0 557
michael@0 558 static Type add(Type* ptr, Type val) {
michael@0 559 return _InterlockedExchangeAdd(ptr, val);
michael@0 560 }
michael@0 561 static Type sub(Type* ptr, Type val) {
michael@0 562 /*
michael@0 563 * _InterlockedExchangeSubtract isn't available before Windows 7,
michael@0 564 * and we must support Windows XP.
michael@0 565 */
michael@0 566 return _InterlockedExchangeAdd(ptr, -val);
michael@0 567 }
michael@0 568 static Type or_(Type* ptr, Type val) {
michael@0 569 return _InterlockedOr(ptr, val);
michael@0 570 }
michael@0 571 static Type xor_(Type* ptr, Type val) {
michael@0 572 return _InterlockedXor(ptr, val);
michael@0 573 }
michael@0 574 static Type and_(Type* ptr, Type val) {
michael@0 575 return _InterlockedAnd(ptr, val);
michael@0 576 }
michael@0 577 static void store(Type* ptr, Type val) {
michael@0 578 _InterlockedExchange(ptr, val);
michael@0 579 }
michael@0 580 static Type exchange(Type* ptr, Type val) {
michael@0 581 return _InterlockedExchange(ptr, val);
michael@0 582 }
michael@0 583 static bool compareExchange(Type* ptr, Type oldVal, Type newVal) {
michael@0 584 return _InterlockedCompareExchange(ptr, newVal, oldVal) == oldVal;
michael@0 585 }
michael@0 586 };
michael@0 587
michael@0 588 # if defined(_M_X64)
michael@0 589
michael@0 590 extern "C" {
michael@0 591 long long __cdecl _InterlockedExchangeAdd64(long long volatile* dst,
michael@0 592 long long value);
michael@0 593 long long __cdecl _InterlockedOr64(long long volatile* dst,
michael@0 594 long long value);
michael@0 595 long long __cdecl _InterlockedXor64(long long volatile* dst,
michael@0 596 long long value);
michael@0 597 long long __cdecl _InterlockedAnd64(long long volatile* dst,
michael@0 598 long long value);
michael@0 599 long long __cdecl _InterlockedExchange64(long long volatile* dst,
michael@0 600 long long value);
michael@0 601 long long __cdecl _InterlockedCompareExchange64(long long volatile* dst,
michael@0 602 long long newVal,
michael@0 603 long long oldVal);
michael@0 604 }
michael@0 605
michael@0 606 # pragma intrinsic(_InterlockedExchangeAdd64)
michael@0 607 # pragma intrinsic(_InterlockedOr64)
michael@0 608 # pragma intrinsic(_InterlockedXor64)
michael@0 609 # pragma intrinsic(_InterlockedAnd64)
michael@0 610 # pragma intrinsic(_InterlockedExchange64)
michael@0 611 # pragma intrinsic(_InterlockedCompareExchange64)
michael@0 612
michael@0 613 template <>
michael@0 614 struct PrimitiveIntrinsics<8>
michael@0 615 {
michael@0 616 typedef __int64 Type;
michael@0 617
michael@0 618 static Type add(Type* ptr, Type val) {
michael@0 619 return _InterlockedExchangeAdd64(ptr, val);
michael@0 620 }
michael@0 621 static Type sub(Type* ptr, Type val) {
michael@0 622 /*
michael@0 623 * There is no _InterlockedExchangeSubtract64.
michael@0 624 */
michael@0 625 return _InterlockedExchangeAdd64(ptr, -val);
michael@0 626 }
michael@0 627 static Type or_(Type* ptr, Type val) {
michael@0 628 return _InterlockedOr64(ptr, val);
michael@0 629 }
michael@0 630 static Type xor_(Type* ptr, Type val) {
michael@0 631 return _InterlockedXor64(ptr, val);
michael@0 632 }
michael@0 633 static Type and_(Type* ptr, Type val) {
michael@0 634 return _InterlockedAnd64(ptr, val);
michael@0 635 }
michael@0 636 static void store(Type* ptr, Type val) {
michael@0 637 _InterlockedExchange64(ptr, val);
michael@0 638 }
michael@0 639 static Type exchange(Type* ptr, Type val) {
michael@0 640 return _InterlockedExchange64(ptr, val);
michael@0 641 }
michael@0 642 static bool compareExchange(Type* ptr, Type oldVal, Type newVal) {
michael@0 643 return _InterlockedCompareExchange64(ptr, newVal, oldVal) == oldVal;
michael@0 644 }
michael@0 645 };
michael@0 646
michael@0 647 # endif
michael@0 648
michael@0 649 extern "C" { void _ReadWriteBarrier(); }
michael@0 650
michael@0 651 # pragma intrinsic(_ReadWriteBarrier)
michael@0 652
michael@0 653 template<MemoryOrdering Order> struct Barrier;
michael@0 654
michael@0 655 /*
michael@0 656 * We do not provide an afterStore method in Barrier, as Relaxed and
michael@0 657 * ReleaseAcquire orderings do not require one, and the required barrier
michael@0 658 * for SequentiallyConsistent is handled by PrimitiveIntrinsics.
michael@0 659 */
michael@0 660
michael@0 661 template<>
michael@0 662 struct Barrier<Relaxed>
michael@0 663 {
michael@0 664 static void beforeLoad() {}
michael@0 665 static void afterLoad() {}
michael@0 666 static void beforeStore() {}
michael@0 667 };
michael@0 668
michael@0 669 template<>
michael@0 670 struct Barrier<ReleaseAcquire>
michael@0 671 {
michael@0 672 static void beforeLoad() {}
michael@0 673 static void afterLoad() { _ReadWriteBarrier(); }
michael@0 674 static void beforeStore() { _ReadWriteBarrier(); }
michael@0 675 };
michael@0 676
michael@0 677 template<>
michael@0 678 struct Barrier<SequentiallyConsistent>
michael@0 679 {
michael@0 680 static void beforeLoad() { _ReadWriteBarrier(); }
michael@0 681 static void afterLoad() { _ReadWriteBarrier(); }
michael@0 682 static void beforeStore() { _ReadWriteBarrier(); }
michael@0 683 };
michael@0 684
michael@0 685 template<typename PrimType, typename T>
michael@0 686 struct CastHelper
michael@0 687 {
michael@0 688 static PrimType toPrimType(T val) { return static_cast<PrimType>(val); }
michael@0 689 static T fromPrimType(PrimType val) { return static_cast<T>(val); }
michael@0 690 };
michael@0 691
michael@0 692 template<typename PrimType, typename T>
michael@0 693 struct CastHelper<PrimType, T*>
michael@0 694 {
michael@0 695 static PrimType toPrimType(T* val) { return reinterpret_cast<PrimType>(val); }
michael@0 696 static T* fromPrimType(PrimType val) { return reinterpret_cast<T*>(val); }
michael@0 697 };
michael@0 698
michael@0 699 template<typename T>
michael@0 700 struct IntrinsicBase
michael@0 701 {
michael@0 702 typedef T ValueType;
michael@0 703 typedef PrimitiveIntrinsics<sizeof(T)> Primitives;
michael@0 704 typedef typename Primitives::Type PrimType;
michael@0 705 static_assert(sizeof(PrimType) == sizeof(T),
michael@0 706 "Selection of PrimitiveIntrinsics was wrong");
michael@0 707 typedef CastHelper<PrimType, T> Cast;
michael@0 708 };
michael@0 709
michael@0 710 template<typename T, MemoryOrdering Order>
michael@0 711 struct IntrinsicMemoryOps : public IntrinsicBase<T>
michael@0 712 {
michael@0 713 typedef typename IntrinsicBase<T>::ValueType ValueType;
michael@0 714 typedef typename IntrinsicBase<T>::Primitives Primitives;
michael@0 715 typedef typename IntrinsicBase<T>::PrimType PrimType;
michael@0 716 typedef typename IntrinsicBase<T>::Cast Cast;
michael@0 717 static ValueType load(const ValueType& ptr) {
michael@0 718 Barrier<Order>::beforeLoad();
michael@0 719 ValueType val = ptr;
michael@0 720 Barrier<Order>::afterLoad();
michael@0 721 return val;
michael@0 722 }
michael@0 723 static void store(ValueType& ptr, ValueType val) {
michael@0 724 // For SequentiallyConsistent, Primitives::store() will generate the
michael@0 725 // proper memory fence. Everything else just needs a barrier before
michael@0 726 // the store.
michael@0 727 if (Order == SequentiallyConsistent) {
michael@0 728 Primitives::store(reinterpret_cast<PrimType*>(&ptr),
michael@0 729 Cast::toPrimType(val));
michael@0 730 } else {
michael@0 731 Barrier<Order>::beforeStore();
michael@0 732 ptr = val;
michael@0 733 }
michael@0 734 }
michael@0 735 static ValueType exchange(ValueType& ptr, ValueType val) {
michael@0 736 PrimType oldval =
michael@0 737 Primitives::exchange(reinterpret_cast<PrimType*>(&ptr),
michael@0 738 Cast::toPrimType(val));
michael@0 739 return Cast::fromPrimType(oldval);
michael@0 740 }
michael@0 741 static bool compareExchange(ValueType& ptr, ValueType oldVal, ValueType newVal) {
michael@0 742 return Primitives::compareExchange(reinterpret_cast<PrimType*>(&ptr),
michael@0 743 Cast::toPrimType(oldVal),
michael@0 744 Cast::toPrimType(newVal));
michael@0 745 }
michael@0 746 };
michael@0 747
michael@0 748 template<typename T>
michael@0 749 struct IntrinsicApplyHelper : public IntrinsicBase<T>
michael@0 750 {
michael@0 751 typedef typename IntrinsicBase<T>::ValueType ValueType;
michael@0 752 typedef typename IntrinsicBase<T>::PrimType PrimType;
michael@0 753 typedef typename IntrinsicBase<T>::Cast Cast;
michael@0 754 typedef PrimType (*BinaryOp)(PrimType*, PrimType);
michael@0 755 typedef PrimType (*UnaryOp)(PrimType*);
michael@0 756
michael@0 757 static ValueType applyBinaryFunction(BinaryOp op, ValueType& ptr,
michael@0 758 ValueType val) {
michael@0 759 PrimType* primTypePtr = reinterpret_cast<PrimType*>(&ptr);
michael@0 760 PrimType primTypeVal = Cast::toPrimType(val);
michael@0 761 return Cast::fromPrimType(op(primTypePtr, primTypeVal));
michael@0 762 }
michael@0 763
michael@0 764 static ValueType applyUnaryFunction(UnaryOp op, ValueType& ptr) {
michael@0 765 PrimType* primTypePtr = reinterpret_cast<PrimType*>(&ptr);
michael@0 766 return Cast::fromPrimType(op(primTypePtr));
michael@0 767 }
michael@0 768 };
michael@0 769
michael@0 770 template<typename T>
michael@0 771 struct IntrinsicAddSub : public IntrinsicApplyHelper<T>
michael@0 772 {
michael@0 773 typedef typename IntrinsicApplyHelper<T>::ValueType ValueType;
michael@0 774 typedef typename IntrinsicBase<T>::Primitives Primitives;
michael@0 775 static ValueType add(ValueType& ptr, ValueType val) {
michael@0 776 return applyBinaryFunction(&Primitives::add, ptr, val);
michael@0 777 }
michael@0 778 static ValueType sub(ValueType& ptr, ValueType val) {
michael@0 779 return applyBinaryFunction(&Primitives::sub, ptr, val);
michael@0 780 }
michael@0 781 };
michael@0 782
michael@0 783 template<typename T>
michael@0 784 struct IntrinsicAddSub<T*> : public IntrinsicApplyHelper<T*>
michael@0 785 {
michael@0 786 typedef typename IntrinsicApplyHelper<T*>::ValueType ValueType;
michael@0 787 static ValueType add(ValueType& ptr, ptrdiff_t amount) {
michael@0 788 return applyBinaryFunction(&Primitives::add, ptr,
michael@0 789 (ValueType)(amount * sizeof(ValueType)));
michael@0 790 }
michael@0 791 static ValueType sub(ValueType& ptr, ptrdiff_t amount) {
michael@0 792 return applyBinaryFunction(&Primitives::sub, ptr,
michael@0 793 (ValueType)(amount * sizeof(ValueType)));
michael@0 794 }
michael@0 795 };
michael@0 796
michael@0 797 template<typename T>
michael@0 798 struct IntrinsicIncDec : public IntrinsicAddSub<T>
michael@0 799 {
michael@0 800 typedef typename IntrinsicAddSub<T>::ValueType ValueType;
michael@0 801 static ValueType inc(ValueType& ptr) { return add(ptr, 1); }
michael@0 802 static ValueType dec(ValueType& ptr) { return sub(ptr, 1); }
michael@0 803 };
michael@0 804
michael@0 805 template<typename T, MemoryOrdering Order>
michael@0 806 struct AtomicIntrinsics : public IntrinsicMemoryOps<T, Order>,
michael@0 807 public IntrinsicIncDec<T>
michael@0 808 {
michael@0 809 typedef typename IntrinsicIncDec<T>::ValueType ValueType;
michael@0 810 static ValueType or_(ValueType& ptr, T val) {
michael@0 811 return applyBinaryFunction(&Primitives::or_, ptr, val);
michael@0 812 }
michael@0 813 static ValueType xor_(ValueType& ptr, T val) {
michael@0 814 return applyBinaryFunction(&Primitives::xor_, ptr, val);
michael@0 815 }
michael@0 816 static ValueType and_(ValueType& ptr, T val) {
michael@0 817 return applyBinaryFunction(&Primitives::and_, ptr, val);
michael@0 818 }
michael@0 819 };
michael@0 820
michael@0 821 template<typename T, MemoryOrdering Order>
michael@0 822 struct AtomicIntrinsics<T*, Order> : public IntrinsicMemoryOps<T*, Order>,
michael@0 823 public IntrinsicIncDec<T*>
michael@0 824 {
michael@0 825 typedef typename IntrinsicMemoryOps<T*, Order>::ValueType ValueType;
michael@0 826 };
michael@0 827
michael@0 828 } // namespace detail
michael@0 829 } // namespace mozilla
michael@0 830
michael@0 831 #else
michael@0 832 # error "Atomic compiler intrinsics are not supported on your platform"
michael@0 833 #endif
michael@0 834
michael@0 835 namespace mozilla {
michael@0 836
michael@0 837 namespace detail {
michael@0 838
michael@0 839 template<typename T, MemoryOrdering Order>
michael@0 840 class AtomicBase
michael@0 841 {
michael@0 842 // We only support 32-bit types on 32-bit Windows, which constrains our
michael@0 843 // implementation elsewhere. But we support pointer-sized types everywhere.
michael@0 844 static_assert(sizeof(T) == 4 || (sizeof(uintptr_t) == 8 && sizeof(T) == 8),
michael@0 845 "mozilla/Atomics.h only supports 32-bit and pointer-sized types");
michael@0 846
michael@0 847 protected:
michael@0 848 typedef typename detail::AtomicIntrinsics<T, Order> Intrinsics;
michael@0 849 typename Intrinsics::ValueType mValue;
michael@0 850
michael@0 851 public:
michael@0 852 MOZ_CONSTEXPR AtomicBase() : mValue() {}
michael@0 853 MOZ_CONSTEXPR AtomicBase(T aInit) : mValue(aInit) {}
michael@0 854
michael@0 855 // Note: we can't provide operator T() here because Atomic<bool> inherits
michael@0 856 // from AtomcBase with T=uint32_t and not T=bool. If we implemented
michael@0 857 // operator T() here, it would cause errors when comparing Atomic<bool> with
michael@0 858 // a regular bool.
michael@0 859
michael@0 860 T operator=(T aValue) {
michael@0 861 Intrinsics::store(mValue, aValue);
michael@0 862 return aValue;
michael@0 863 }
michael@0 864
michael@0 865 /**
michael@0 866 * Performs an atomic swap operation. aValue is stored and the previous
michael@0 867 * value of this variable is returned.
michael@0 868 */
michael@0 869 T exchange(T aValue) {
michael@0 870 return Intrinsics::exchange(mValue, aValue);
michael@0 871 }
michael@0 872
michael@0 873 /**
michael@0 874 * Performs an atomic compare-and-swap operation and returns true if it
michael@0 875 * succeeded. This is equivalent to atomically doing
michael@0 876 *
michael@0 877 * if (mValue == aOldValue) {
michael@0 878 * mValue = aNewValue;
michael@0 879 * return true;
michael@0 880 * } else {
michael@0 881 * return false;
michael@0 882 * }
michael@0 883 */
michael@0 884 bool compareExchange(T aOldValue, T aNewValue) {
michael@0 885 return Intrinsics::compareExchange(mValue, aOldValue, aNewValue);
michael@0 886 }
michael@0 887
michael@0 888 private:
michael@0 889 template<MemoryOrdering AnyOrder>
michael@0 890 AtomicBase(const AtomicBase<T, AnyOrder>& aCopy) MOZ_DELETE;
michael@0 891 };
michael@0 892
michael@0 893 template<typename T, MemoryOrdering Order>
michael@0 894 class AtomicBaseIncDec : public AtomicBase<T, Order>
michael@0 895 {
michael@0 896 typedef typename detail::AtomicBase<T, Order> Base;
michael@0 897
michael@0 898 public:
michael@0 899 MOZ_CONSTEXPR AtomicBaseIncDec() : Base() {}
michael@0 900 MOZ_CONSTEXPR AtomicBaseIncDec(T aInit) : Base(aInit) {}
michael@0 901
michael@0 902 using Base::operator=;
michael@0 903
michael@0 904 operator T() const { return Base::Intrinsics::load(Base::mValue); }
michael@0 905 T operator++(int) { return Base::Intrinsics::inc(Base::mValue); }
michael@0 906 T operator--(int) { return Base::Intrinsics::dec(Base::mValue); }
michael@0 907 T operator++() { return Base::Intrinsics::inc(Base::mValue) + 1; }
michael@0 908 T operator--() { return Base::Intrinsics::dec(Base::mValue) - 1; }
michael@0 909
michael@0 910 private:
michael@0 911 template<MemoryOrdering AnyOrder>
michael@0 912 AtomicBaseIncDec(const AtomicBaseIncDec<T, AnyOrder>& aCopy) MOZ_DELETE;
michael@0 913 };
michael@0 914
michael@0 915 } // namespace detail
michael@0 916
michael@0 917 /**
michael@0 918 * A wrapper for a type that enforces that all memory accesses are atomic.
michael@0 919 *
michael@0 920 * In general, where a variable |T foo| exists, |Atomic<T> foo| can be used in
michael@0 921 * its place. Implementations for integral and pointer types are provided
michael@0 922 * below.
michael@0 923 *
michael@0 924 * Atomic accesses are sequentially consistent by default. You should
michael@0 925 * use the default unless you are tall enough to ride the
michael@0 926 * memory-ordering roller coaster (if you're not sure, you aren't) and
michael@0 927 * you have a compelling reason to do otherwise.
michael@0 928 *
michael@0 929 * There is one exception to the case of atomic memory accesses: providing an
michael@0 930 * initial value of the atomic value is not guaranteed to be atomic. This is a
michael@0 931 * deliberate design choice that enables static atomic variables to be declared
michael@0 932 * without introducing extra static constructors.
michael@0 933 */
michael@0 934 template<typename T,
michael@0 935 MemoryOrdering Order = SequentiallyConsistent,
michael@0 936 typename Enable = void>
michael@0 937 class Atomic;
michael@0 938
michael@0 939 /**
michael@0 940 * Atomic<T> implementation for integral types.
michael@0 941 *
michael@0 942 * In addition to atomic store and load operations, compound assignment and
michael@0 943 * increment/decrement operators are implemented which perform the
michael@0 944 * corresponding read-modify-write operation atomically. Finally, an atomic
michael@0 945 * swap method is provided.
michael@0 946 */
michael@0 947 template<typename T, MemoryOrdering Order>
michael@0 948 class Atomic<T, Order, typename EnableIf<IsIntegral<T>::value && !IsSame<T, bool>::value>::Type>
michael@0 949 : public detail::AtomicBaseIncDec<T, Order>
michael@0 950 {
michael@0 951 typedef typename detail::AtomicBaseIncDec<T, Order> Base;
michael@0 952
michael@0 953 public:
michael@0 954 MOZ_CONSTEXPR Atomic() : Base() {}
michael@0 955 MOZ_CONSTEXPR Atomic(T aInit) : Base(aInit) {}
michael@0 956
michael@0 957 using Base::operator=;
michael@0 958
michael@0 959 T operator+=(T delta) { return Base::Intrinsics::add(Base::mValue, delta) + delta; }
michael@0 960 T operator-=(T delta) { return Base::Intrinsics::sub(Base::mValue, delta) - delta; }
michael@0 961 T operator|=(T val) { return Base::Intrinsics::or_(Base::mValue, val) | val; }
michael@0 962 T operator^=(T val) { return Base::Intrinsics::xor_(Base::mValue, val) ^ val; }
michael@0 963 T operator&=(T val) { return Base::Intrinsics::and_(Base::mValue, val) & val; }
michael@0 964
michael@0 965 private:
michael@0 966 Atomic(Atomic<T, Order>& aOther) MOZ_DELETE;
michael@0 967 };
michael@0 968
michael@0 969 /**
michael@0 970 * Atomic<T> implementation for pointer types.
michael@0 971 *
michael@0 972 * An atomic compare-and-swap primitive for pointer variables is provided, as
michael@0 973 * are atomic increment and decement operators. Also provided are the compound
michael@0 974 * assignment operators for addition and subtraction. Atomic swap (via
michael@0 975 * exchange()) is included as well.
michael@0 976 */
michael@0 977 template<typename T, MemoryOrdering Order>
michael@0 978 class Atomic<T*, Order> : public detail::AtomicBaseIncDec<T*, Order>
michael@0 979 {
michael@0 980 typedef typename detail::AtomicBaseIncDec<T*, Order> Base;
michael@0 981
michael@0 982 public:
michael@0 983 MOZ_CONSTEXPR Atomic() : Base() {}
michael@0 984 MOZ_CONSTEXPR Atomic(T* aInit) : Base(aInit) {}
michael@0 985
michael@0 986 using Base::operator=;
michael@0 987
michael@0 988 T* operator+=(ptrdiff_t delta) {
michael@0 989 return Base::Intrinsics::add(Base::mValue, delta) + delta;
michael@0 990 }
michael@0 991 T* operator-=(ptrdiff_t delta) {
michael@0 992 return Base::Intrinsics::sub(Base::mValue, delta) - delta;
michael@0 993 }
michael@0 994
michael@0 995 private:
michael@0 996 Atomic(Atomic<T*, Order>& aOther) MOZ_DELETE;
michael@0 997 };
michael@0 998
michael@0 999 /**
michael@0 1000 * Atomic<T> implementation for enum types.
michael@0 1001 *
michael@0 1002 * The atomic store and load operations and the atomic swap method is provided.
michael@0 1003 */
michael@0 1004 template<typename T, MemoryOrdering Order>
michael@0 1005 class Atomic<T, Order, typename EnableIf<IsEnum<T>::value>::Type>
michael@0 1006 : public detail::AtomicBase<T, Order>
michael@0 1007 {
michael@0 1008 typedef typename detail::AtomicBase<T, Order> Base;
michael@0 1009
michael@0 1010 public:
michael@0 1011 MOZ_CONSTEXPR Atomic() : Base() {}
michael@0 1012 MOZ_CONSTEXPR Atomic(T aInit) : Base(aInit) {}
michael@0 1013
michael@0 1014 operator T() const { return Base::Intrinsics::load(Base::mValue); }
michael@0 1015
michael@0 1016 using Base::operator=;
michael@0 1017
michael@0 1018 private:
michael@0 1019 Atomic(Atomic<T, Order>& aOther) MOZ_DELETE;
michael@0 1020 };
michael@0 1021
michael@0 1022 /**
michael@0 1023 * Atomic<T> implementation for boolean types.
michael@0 1024 *
michael@0 1025 * The atomic store and load operations and the atomic swap method is provided.
michael@0 1026 *
michael@0 1027 * Note:
michael@0 1028 *
michael@0 1029 * - sizeof(Atomic<bool>) != sizeof(bool) for some implementations of
michael@0 1030 * bool and/or some implementations of std::atomic. This is allowed in
michael@0 1031 * [atomic.types.generic]p9.
michael@0 1032 *
michael@0 1033 * - It's not obvious whether the 8-bit atomic functions on Windows are always
michael@0 1034 * inlined or not. If they are not inlined, the corresponding functions in the
michael@0 1035 * runtime library are not available on Windows XP. This is why we implement
michael@0 1036 * Atomic<bool> with an underlying type of uint32_t.
michael@0 1037 */
michael@0 1038 template<MemoryOrdering Order>
michael@0 1039 class Atomic<bool, Order>
michael@0 1040 : protected detail::AtomicBase<uint32_t, Order>
michael@0 1041 {
michael@0 1042 typedef typename detail::AtomicBase<uint32_t, Order> Base;
michael@0 1043
michael@0 1044 public:
michael@0 1045 MOZ_CONSTEXPR Atomic() : Base() {}
michael@0 1046 MOZ_CONSTEXPR Atomic(bool aInit) : Base(aInit) {}
michael@0 1047
michael@0 1048 // We provide boolean wrappers for the underlying AtomicBase methods.
michael@0 1049 operator bool() const { return Base::Intrinsics::load(Base::mValue); }
michael@0 1050 bool operator=(bool aValue) { return Base::operator=(aValue); }
michael@0 1051 bool exchange(bool aValue) { return Base::exchange(aValue); }
michael@0 1052 bool compareExchange(bool aOldValue, bool aNewValue) {
michael@0 1053 return Base::compareExchange(aOldValue, aNewValue);
michael@0 1054 }
michael@0 1055
michael@0 1056 private:
michael@0 1057 Atomic(Atomic<bool, Order>& aOther) MOZ_DELETE;
michael@0 1058 };
michael@0 1059
michael@0 1060 } // namespace mozilla
michael@0 1061
michael@0 1062 #endif /* mozilla_Atomics_h */

mercurial