mfbt/Atomics.h

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/mfbt/Atomics.h	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1062 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim: set ts=8 sts=2 et sw=2 tw=80: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +/*
    1.11 + * Implements (almost always) lock-free atomic operations. The operations here
    1.12 + * are a subset of that which can be found in C++11's <atomic> header, with a
    1.13 + * different API to enforce consistent memory ordering constraints.
    1.14 + *
    1.15 + * Anyone caught using |volatile| for inter-thread memory safety needs to be
    1.16 + * sent a copy of this header and the C++11 standard.
    1.17 + */
    1.18 +
    1.19 +#ifndef mozilla_Atomics_h
    1.20 +#define mozilla_Atomics_h
    1.21 +
    1.22 +#include "mozilla/Assertions.h"
    1.23 +#include "mozilla/Attributes.h"
    1.24 +#include "mozilla/Compiler.h"
    1.25 +#include "mozilla/TypeTraits.h"
    1.26 +
    1.27 +#include <stdint.h>
    1.28 +
    1.29 +/*
    1.30 + * Our minimum deployment target on clang/OS X is OS X 10.6, whose SDK
    1.31 + * does not have <atomic>.  So be sure to check for <atomic> support
    1.32 + * along with C++0x support.
    1.33 + */
    1.34 +#if defined(__clang__) || defined(__GNUC__)
    1.35 +   /*
    1.36 +    * Clang doesn't like <atomic> from libstdc++ before 4.7 due to the
    1.37 +    * loose typing of the atomic builtins. GCC 4.5 and 4.6 lacks inline
    1.38 +    * definitions for unspecialized std::atomic and causes linking errors.
    1.39 +    * Therefore, we require at least 4.7.0 for using libstdc++.
    1.40 +    */
    1.41 +#  if MOZ_USING_LIBSTDCXX && MOZ_LIBSTDCXX_VERSION_AT_LEAST(4, 7, 0)
    1.42 +#    define MOZ_HAVE_CXX11_ATOMICS
    1.43 +#  elif MOZ_USING_LIBCXX
    1.44 +#    define MOZ_HAVE_CXX11_ATOMICS
    1.45 +#  endif
    1.46 +#elif defined(_MSC_VER) && _MSC_VER >= 1700
    1.47 +#  if defined(DEBUG)
    1.48 +     /*
    1.49 +      * Provide our own failure code since we're having trouble linking to
    1.50 +      * std::_Debug_message (bug 982310).
    1.51 +      */
    1.52 +#    define _INVALID_MEMORY_ORDER MOZ_CRASH("Invalid memory order")
    1.53 +#  endif
    1.54 +#  define MOZ_HAVE_CXX11_ATOMICS
    1.55 +#endif
    1.56 +
    1.57 +namespace mozilla {
    1.58 +
    1.59 +/**
    1.60 + * An enum of memory ordering possibilities for atomics.
    1.61 + *
    1.62 + * Memory ordering is the observable state of distinct values in memory.
    1.63 + * (It's a separate concept from atomicity, which concerns whether an
    1.64 + * operation can ever be observed in an intermediate state.  Don't
    1.65 + * conflate the two!)  Given a sequence of operations in source code on
    1.66 + * memory, it is *not* always the case that, at all times and on all
    1.67 + * cores, those operations will appear to have occurred in that exact
    1.68 + * sequence.  First, the compiler might reorder that sequence, if it
    1.69 + * thinks another ordering will be more efficient.  Second, the CPU may
    1.70 + * not expose so consistent a view of memory.  CPUs will often perform
    1.71 + * their own instruction reordering, above and beyond that performed by
    1.72 + * the compiler.  And each core has its own memory caches, and accesses
    1.73 + * (reads and writes both) to "memory" may only resolve to out-of-date
    1.74 + * cache entries -- not to the "most recently" performed operation in
    1.75 + * some global sense.  Any access to a value that may be used by
    1.76 + * multiple threads, potentially across multiple cores, must therefore
    1.77 + * have a memory ordering imposed on it, for all code on all
    1.78 + * threads/cores to have a sufficiently coherent worldview.
    1.79 + *
    1.80 + * http://gcc.gnu.org/wiki/Atomic/GCCMM/AtomicSync and
    1.81 + * http://en.cppreference.com/w/cpp/atomic/memory_order go into more
    1.82 + * detail on all this, including examples of how each mode works.
    1.83 + *
    1.84 + * Note that for simplicity and practicality, not all of the modes in
    1.85 + * C++11 are supported.  The missing C++11 modes are either subsumed by
    1.86 + * the modes we provide below, or not relevant for the CPUs we support
    1.87 + * in Gecko.  These three modes are confusing enough as it is!
    1.88 + */
    1.89 +enum MemoryOrdering {
    1.90 +  /*
    1.91 +   * Relaxed ordering is the simplest memory ordering: none at all.
    1.92 +   * When the result of a write is observed, nothing may be inferred
    1.93 +   * about other memory.  Writes ostensibly performed "before" on the
    1.94 +   * writing thread may not yet be visible.  Writes performed "after" on
    1.95 +   * the writing thread may already be visible, if the compiler or CPU
    1.96 +   * reordered them.  (The latter can happen if reads and/or writes get
    1.97 +   * held up in per-processor caches.)  Relaxed ordering means
    1.98 +   * operations can always use cached values (as long as the actual
    1.99 +   * updates to atomic values actually occur, correctly, eventually), so
   1.100 +   * it's usually the fastest sort of atomic access.  For this reason,
   1.101 +   * *it's also the most dangerous kind of access*.
   1.102 +   *
   1.103 +   * Relaxed ordering is good for things like process-wide statistics
   1.104 +   * counters that don't need to be consistent with anything else, so
   1.105 +   * long as updates themselves are atomic.  (And so long as any
   1.106 +   * observations of that value can tolerate being out-of-date -- if you
   1.107 +   * need some sort of up-to-date value, you need some sort of other
   1.108 +   * synchronizing operation.)  It's *not* good for locks, mutexes,
   1.109 +   * reference counts, etc. that mediate access to other memory, or must
   1.110 +   * be observably consistent with other memory.
   1.111 +   *
   1.112 +   * x86 architectures don't take advantage of the optimization
   1.113 +   * opportunities that relaxed ordering permits.  Thus it's possible
   1.114 +   * that using relaxed ordering will "work" on x86 but fail elsewhere
   1.115 +   * (ARM, say, which *does* implement non-sequentially-consistent
   1.116 +   * relaxed ordering semantics).  Be extra-careful using relaxed
   1.117 +   * ordering if you can't easily test non-x86 architectures!
   1.118 +   */
   1.119 +  Relaxed,
   1.120 +  /*
   1.121 +   * When an atomic value is updated with ReleaseAcquire ordering, and
   1.122 +   * that new value is observed with ReleaseAcquire ordering, prior
   1.123 +   * writes (atomic or not) are also observable.  What ReleaseAcquire
   1.124 +   * *doesn't* give you is any observable ordering guarantees for
   1.125 +   * ReleaseAcquire-ordered operations on different objects.  For
   1.126 +   * example, if there are two cores that each perform ReleaseAcquire
   1.127 +   * operations on separate objects, each core may or may not observe
   1.128 +   * the operations made by the other core.  The only way the cores can
   1.129 +   * be synchronized with ReleaseAcquire is if they both
   1.130 +   * ReleaseAcquire-access the same object.  This implies that you can't
   1.131 +   * necessarily describe some global total ordering of ReleaseAcquire
   1.132 +   * operations.
   1.133 +   *
   1.134 +   * ReleaseAcquire ordering is good for (as the name implies) atomic
   1.135 +   * operations on values controlling ownership of things: reference
   1.136 +   * counts, mutexes, and the like.  However, if you are thinking about
   1.137 +   * using these to implement your own locks or mutexes, you should take
   1.138 +   * a good, hard look at actual lock or mutex primitives first.
   1.139 +   */
   1.140 +  ReleaseAcquire,
   1.141 +  /*
   1.142 +   * When an atomic value is updated with SequentiallyConsistent
   1.143 +   * ordering, all writes observable when the update is observed, just
   1.144 +   * as with ReleaseAcquire ordering.  But, furthermore, a global total
   1.145 +   * ordering of SequentiallyConsistent operations *can* be described.
   1.146 +   * For example, if two cores perform SequentiallyConsistent operations
   1.147 +   * on separate objects, one core will observably perform its update
   1.148 +   * (and all previous operations will have completed), then the other
   1.149 +   * core will observably perform its update (and all previous
   1.150 +   * operations will have completed).  (Although those previous
   1.151 +   * operations aren't themselves ordered -- they could be intermixed,
   1.152 +   * or ordered if they occur on atomic values with ordering
   1.153 +   * requirements.)  SequentiallyConsistent is the *simplest and safest*
   1.154 +   * ordering of atomic operations -- it's always as if one operation
   1.155 +   * happens, then another, then another, in some order -- and every
   1.156 +   * core observes updates to happen in that single order.  Because it
   1.157 +   * has the most synchronization requirements, operations ordered this
   1.158 +   * way also tend to be slowest.
   1.159 +   *
   1.160 +   * SequentiallyConsistent ordering can be desirable when multiple
   1.161 +   * threads observe objects, and they all have to agree on the
   1.162 +   * observable order of changes to them.  People expect
   1.163 +   * SequentiallyConsistent ordering, even if they shouldn't, when
   1.164 +   * writing code, atomic or otherwise.  SequentiallyConsistent is also
   1.165 +   * the ordering of choice when designing lockless data structures.  If
   1.166 +   * you don't know what order to use, use this one.
   1.167 +   */
   1.168 +  SequentiallyConsistent,
   1.169 +};
   1.170 +
   1.171 +} // namespace mozilla
   1.172 +
   1.173 +// Build up the underlying intrinsics.
   1.174 +#ifdef MOZ_HAVE_CXX11_ATOMICS
   1.175 +
   1.176 +#  include <atomic>
   1.177 +
   1.178 +namespace mozilla {
   1.179 +namespace detail {
   1.180 +
   1.181 +/*
   1.182 + * We provide CompareExchangeFailureOrder to work around a bug in some
   1.183 + * versions of GCC's <atomic> header.  See bug 898491.
   1.184 + */
   1.185 +template<MemoryOrdering Order> struct AtomicOrderConstraints;
   1.186 +
   1.187 +template<>
   1.188 +struct AtomicOrderConstraints<Relaxed>
   1.189 +{
   1.190 +    static const std::memory_order AtomicRMWOrder = std::memory_order_relaxed;
   1.191 +    static const std::memory_order LoadOrder = std::memory_order_relaxed;
   1.192 +    static const std::memory_order StoreOrder = std::memory_order_relaxed;
   1.193 +    static const std::memory_order CompareExchangeFailureOrder =
   1.194 +      std::memory_order_relaxed;
   1.195 +};
   1.196 +
   1.197 +template<>
   1.198 +struct AtomicOrderConstraints<ReleaseAcquire>
   1.199 +{
   1.200 +    static const std::memory_order AtomicRMWOrder = std::memory_order_acq_rel;
   1.201 +    static const std::memory_order LoadOrder = std::memory_order_acquire;
   1.202 +    static const std::memory_order StoreOrder = std::memory_order_release;
   1.203 +    static const std::memory_order CompareExchangeFailureOrder =
   1.204 +      std::memory_order_acquire;
   1.205 +};
   1.206 +
   1.207 +template<>
   1.208 +struct AtomicOrderConstraints<SequentiallyConsistent>
   1.209 +{
   1.210 +    static const std::memory_order AtomicRMWOrder = std::memory_order_seq_cst;
   1.211 +    static const std::memory_order LoadOrder = std::memory_order_seq_cst;
   1.212 +    static const std::memory_order StoreOrder = std::memory_order_seq_cst;
   1.213 +    static const std::memory_order CompareExchangeFailureOrder =
   1.214 +      std::memory_order_seq_cst;
   1.215 +};
   1.216 +
   1.217 +template<typename T, MemoryOrdering Order>
   1.218 +struct IntrinsicBase
   1.219 +{
   1.220 +    typedef std::atomic<T> ValueType;
   1.221 +    typedef AtomicOrderConstraints<Order> OrderedOp;
   1.222 +};
   1.223 +
   1.224 +template<typename T, MemoryOrdering Order>
   1.225 +struct IntrinsicMemoryOps : public IntrinsicBase<T, Order>
   1.226 +{
   1.227 +    typedef IntrinsicBase<T, Order> Base;
   1.228 +    static T load(const typename Base::ValueType& ptr) {
   1.229 +      return ptr.load(Base::OrderedOp::LoadOrder);
   1.230 +    }
   1.231 +    static void store(typename Base::ValueType& ptr, T val) {
   1.232 +      ptr.store(val, Base::OrderedOp::StoreOrder);
   1.233 +    }
   1.234 +    static T exchange(typename Base::ValueType& ptr, T val) {
   1.235 +      return ptr.exchange(val, Base::OrderedOp::AtomicRMWOrder);
   1.236 +    }
   1.237 +    static bool compareExchange(typename Base::ValueType& ptr, T oldVal, T newVal) {
   1.238 +      return ptr.compare_exchange_strong(oldVal, newVal,
   1.239 +                                         Base::OrderedOp::AtomicRMWOrder,
   1.240 +                                         Base::OrderedOp::CompareExchangeFailureOrder);
   1.241 +    }
   1.242 +};
   1.243 +
   1.244 +template<typename T, MemoryOrdering Order>
   1.245 +struct IntrinsicAddSub : public IntrinsicBase<T, Order>
   1.246 +{
   1.247 +    typedef IntrinsicBase<T, Order> Base;
   1.248 +    static T add(typename Base::ValueType& ptr, T val) {
   1.249 +      return ptr.fetch_add(val, Base::OrderedOp::AtomicRMWOrder);
   1.250 +    }
   1.251 +    static T sub(typename Base::ValueType& ptr, T val) {
   1.252 +      return ptr.fetch_sub(val, Base::OrderedOp::AtomicRMWOrder);
   1.253 +    }
   1.254 +};
   1.255 +
   1.256 +template<typename T, MemoryOrdering Order>
   1.257 +struct IntrinsicAddSub<T*, Order> : public IntrinsicBase<T*, Order>
   1.258 +{
   1.259 +    typedef IntrinsicBase<T*, Order> Base;
   1.260 +    static T* add(typename Base::ValueType& ptr, ptrdiff_t val) {
   1.261 +      return ptr.fetch_add(fixupAddend(val), Base::OrderedOp::AtomicRMWOrder);
   1.262 +    }
   1.263 +    static T* sub(typename Base::ValueType& ptr, ptrdiff_t val) {
   1.264 +      return ptr.fetch_sub(fixupAddend(val), Base::OrderedOp::AtomicRMWOrder);
   1.265 +    }
   1.266 +  private:
   1.267 +    /*
   1.268 +     * GCC 4.6's <atomic> header has a bug where adding X to an
   1.269 +     * atomic<T*> is not the same as adding X to a T*.  Hence the need
   1.270 +     * for this function to provide the correct addend.
   1.271 +     */
   1.272 +    static ptrdiff_t fixupAddend(ptrdiff_t val) {
   1.273 +#if defined(__clang__) || defined(_MSC_VER)
   1.274 +      return val;
   1.275 +#elif defined(__GNUC__) && MOZ_GCC_VERSION_AT_LEAST(4, 6, 0) && \
   1.276 +      !MOZ_GCC_VERSION_AT_LEAST(4, 7, 0)
   1.277 +      return val * sizeof(T);
   1.278 +#else
   1.279 +      return val;
   1.280 +#endif
   1.281 +    }
   1.282 +};
   1.283 +
   1.284 +template<typename T, MemoryOrdering Order>
   1.285 +struct IntrinsicIncDec : public IntrinsicAddSub<T, Order>
   1.286 +{
   1.287 +    typedef IntrinsicBase<T, Order> Base;
   1.288 +    static T inc(typename Base::ValueType& ptr) {
   1.289 +      return IntrinsicAddSub<T, Order>::add(ptr, 1);
   1.290 +    }
   1.291 +    static T dec(typename Base::ValueType& ptr) {
   1.292 +      return IntrinsicAddSub<T, Order>::sub(ptr, 1);
   1.293 +    }
   1.294 +};
   1.295 +
   1.296 +template<typename T, MemoryOrdering Order>
   1.297 +struct AtomicIntrinsics : public IntrinsicMemoryOps<T, Order>,
   1.298 +                          public IntrinsicIncDec<T, Order>
   1.299 +{
   1.300 +    typedef IntrinsicBase<T, Order> Base;
   1.301 +    static T or_(typename Base::ValueType& ptr, T val) {
   1.302 +      return ptr.fetch_or(val, Base::OrderedOp::AtomicRMWOrder);
   1.303 +    }
   1.304 +    static T xor_(typename Base::ValueType& ptr, T val) {
   1.305 +      return ptr.fetch_xor(val, Base::OrderedOp::AtomicRMWOrder);
   1.306 +    }
   1.307 +    static T and_(typename Base::ValueType& ptr, T val) {
   1.308 +      return ptr.fetch_and(val, Base::OrderedOp::AtomicRMWOrder);
   1.309 +    }
   1.310 +};
   1.311 +
   1.312 +template<typename T, MemoryOrdering Order>
   1.313 +struct AtomicIntrinsics<T*, Order>
   1.314 +  : public IntrinsicMemoryOps<T*, Order>, public IntrinsicIncDec<T*, Order>
   1.315 +{
   1.316 +};
   1.317 +
   1.318 +} // namespace detail
   1.319 +} // namespace mozilla
   1.320 +
   1.321 +#elif defined(__GNUC__)
   1.322 +
   1.323 +namespace mozilla {
   1.324 +namespace detail {
   1.325 +
   1.326 +/*
   1.327 + * The __sync_* family of intrinsics is documented here:
   1.328 + *
   1.329 + * http://gcc.gnu.org/onlinedocs/gcc-4.6.4/gcc/Atomic-Builtins.html
   1.330 + *
   1.331 + * While these intrinsics are deprecated in favor of the newer __atomic_*
   1.332 + * family of intrincs:
   1.333 + *
   1.334 + * http://gcc.gnu.org/onlinedocs/gcc-4.7.3/gcc/_005f_005fatomic-Builtins.html
   1.335 + *
   1.336 + * any GCC version that supports the __atomic_* intrinsics will also support
   1.337 + * the <atomic> header and so will be handled above.  We provide a version of
   1.338 + * atomics using the __sync_* intrinsics to support older versions of GCC.
   1.339 + *
   1.340 + * All __sync_* intrinsics that we use below act as full memory barriers, for
   1.341 + * both compiler and hardware reordering, except for __sync_lock_test_and_set,
   1.342 + * which is a only an acquire barrier.  When we call __sync_lock_test_and_set,
   1.343 + * we add a barrier above it as appropriate.
   1.344 + */
   1.345 +
   1.346 +template<MemoryOrdering Order> struct Barrier;
   1.347 +
   1.348 +/*
   1.349 + * Some processors (in particular, x86) don't require quite so many calls to
   1.350 + * __sync_sychronize as our specializations of Barrier produce.  If
   1.351 + * performance turns out to be an issue, defining these specializations
   1.352 + * on a per-processor basis would be a good first tuning step.
   1.353 + */
   1.354 +
   1.355 +template<>
   1.356 +struct Barrier<Relaxed>
   1.357 +{
   1.358 +    static void beforeLoad() {}
   1.359 +    static void afterLoad() {}
   1.360 +    static void beforeStore() {}
   1.361 +    static void afterStore() {}
   1.362 +};
   1.363 +
   1.364 +template<>
   1.365 +struct Barrier<ReleaseAcquire>
   1.366 +{
   1.367 +    static void beforeLoad() {}
   1.368 +    static void afterLoad() { __sync_synchronize(); }
   1.369 +    static void beforeStore() { __sync_synchronize(); }
   1.370 +    static void afterStore() {}
   1.371 +};
   1.372 +
   1.373 +template<>
   1.374 +struct Barrier<SequentiallyConsistent>
   1.375 +{
   1.376 +    static void beforeLoad() { __sync_synchronize(); }
   1.377 +    static void afterLoad() { __sync_synchronize(); }
   1.378 +    static void beforeStore() { __sync_synchronize(); }
   1.379 +    static void afterStore() { __sync_synchronize(); }
   1.380 +};
   1.381 +
   1.382 +template<typename T, MemoryOrdering Order>
   1.383 +struct IntrinsicMemoryOps
   1.384 +{
   1.385 +    static T load(const T& ptr) {
   1.386 +      Barrier<Order>::beforeLoad();
   1.387 +      T val = ptr;
   1.388 +      Barrier<Order>::afterLoad();
   1.389 +      return val;
   1.390 +    }
   1.391 +    static void store(T& ptr, T val) {
   1.392 +      Barrier<Order>::beforeStore();
   1.393 +      ptr = val;
   1.394 +      Barrier<Order>::afterStore();
   1.395 +    }
   1.396 +    static T exchange(T& ptr, T val) {
   1.397 +      // __sync_lock_test_and_set is only an acquire barrier; loads and stores
   1.398 +      // can't be moved up from after to before it, but they can be moved down
   1.399 +      // from before to after it.  We may want a stricter ordering, so we need
   1.400 +      // an explicit barrier.
   1.401 +
   1.402 +      Barrier<Order>::beforeStore();
   1.403 +      return __sync_lock_test_and_set(&ptr, val);
   1.404 +    }
   1.405 +    static bool compareExchange(T& ptr, T oldVal, T newVal) {
   1.406 +      return __sync_bool_compare_and_swap(&ptr, oldVal, newVal);
   1.407 +    }
   1.408 +};
   1.409 +
   1.410 +template<typename T>
   1.411 +struct IntrinsicAddSub
   1.412 +{
   1.413 +    typedef T ValueType;
   1.414 +    static T add(T& ptr, T val) {
   1.415 +      return __sync_fetch_and_add(&ptr, val);
   1.416 +    }
   1.417 +    static T sub(T& ptr, T val) {
   1.418 +      return __sync_fetch_and_sub(&ptr, val);
   1.419 +    }
   1.420 +};
   1.421 +
   1.422 +template<typename T>
   1.423 +struct IntrinsicAddSub<T*>
   1.424 +{
   1.425 +    typedef T* ValueType;
   1.426 +    /*
   1.427 +     * The reinterpret_casts are needed so that
   1.428 +     * __sync_fetch_and_{add,sub} will properly type-check.
   1.429 +     *
   1.430 +     * Also, these functions do not provide standard semantics for
   1.431 +     * pointer types, so we need to adjust the addend.
   1.432 +     */
   1.433 +    static ValueType add(ValueType& ptr, ptrdiff_t val) {
   1.434 +      ValueType amount = reinterpret_cast<ValueType>(val * sizeof(T));
   1.435 +      return __sync_fetch_and_add(&ptr, amount);
   1.436 +    }
   1.437 +    static ValueType sub(ValueType& ptr, ptrdiff_t val) {
   1.438 +      ValueType amount = reinterpret_cast<ValueType>(val * sizeof(T));
   1.439 +      return __sync_fetch_and_sub(&ptr, amount);
   1.440 +    }
   1.441 +};
   1.442 +
   1.443 +template<typename T>
   1.444 +struct IntrinsicIncDec : public IntrinsicAddSub<T>
   1.445 +{
   1.446 +    static T inc(T& ptr) { return IntrinsicAddSub<T>::add(ptr, 1); }
   1.447 +    static T dec(T& ptr) { return IntrinsicAddSub<T>::sub(ptr, 1); }
   1.448 +};
   1.449 +
   1.450 +template<typename T, MemoryOrdering Order>
   1.451 +struct AtomicIntrinsics : public IntrinsicMemoryOps<T, Order>,
   1.452 +                          public IntrinsicIncDec<T>
   1.453 +{
   1.454 +    static T or_(T& ptr, T val) {
   1.455 +      return __sync_fetch_and_or(&ptr, val);
   1.456 +    }
   1.457 +    static T xor_(T& ptr, T val) {
   1.458 +      return __sync_fetch_and_xor(&ptr, val);
   1.459 +    }
   1.460 +    static T and_(T& ptr, T val) {
   1.461 +      return __sync_fetch_and_and(&ptr, val);
   1.462 +    }
   1.463 +};
   1.464 +
   1.465 +template<typename T, MemoryOrdering Order>
   1.466 +struct AtomicIntrinsics<T*, Order> : public IntrinsicMemoryOps<T*, Order>,
   1.467 +                                     public IntrinsicIncDec<T*>
   1.468 +{
   1.469 +};
   1.470 +
   1.471 +} // namespace detail
   1.472 +} // namespace mozilla
   1.473 +
   1.474 +#elif defined(_MSC_VER)
   1.475 +
   1.476 +/*
   1.477 + * Windows comes with a full complement of atomic operations.
   1.478 + * Unfortunately, most of those aren't available for Windows XP (even if
   1.479 + * the compiler supports intrinsics for them), which is the oldest
   1.480 + * version of Windows we support.  Therefore, we only provide operations
   1.481 + * on 32-bit datatypes for 32-bit Windows versions; for 64-bit Windows
   1.482 + * versions, we support 64-bit datatypes as well.
   1.483 + *
   1.484 + * To avoid namespace pollution issues, we declare whatever functions we
   1.485 + * need ourselves.
   1.486 + */
   1.487 +
   1.488 +extern "C" {
   1.489 +long __cdecl _InterlockedExchangeAdd(long volatile* dst, long value);
   1.490 +long __cdecl _InterlockedOr(long volatile* dst, long value);
   1.491 +long __cdecl _InterlockedXor(long volatile* dst, long value);
   1.492 +long __cdecl _InterlockedAnd(long volatile* dst, long value);
   1.493 +long __cdecl _InterlockedExchange(long volatile *dst, long value);
   1.494 +long __cdecl _InterlockedCompareExchange(long volatile *dst, long newVal, long oldVal);
   1.495 +}
   1.496 +
   1.497 +#  pragma intrinsic(_InterlockedExchangeAdd)
   1.498 +#  pragma intrinsic(_InterlockedOr)
   1.499 +#  pragma intrinsic(_InterlockedXor)
   1.500 +#  pragma intrinsic(_InterlockedAnd)
   1.501 +#  pragma intrinsic(_InterlockedExchange)
   1.502 +#  pragma intrinsic(_InterlockedCompareExchange)
   1.503 +
   1.504 +namespace mozilla {
   1.505 +namespace detail {
   1.506 +
   1.507 +#  if !defined(_M_IX86) && !defined(_M_X64)
   1.508 +     /*
   1.509 +      * The implementations below are optimized for x86ish systems.  You
   1.510 +      * will have to modify them if you are porting to Windows on a
   1.511 +      * different architecture.
   1.512 +      */
   1.513 +#    error "Unknown CPU type"
   1.514 +#  endif
   1.515 +
   1.516 +/*
   1.517 + * The PrimitiveIntrinsics template should define |Type|, the datatype of size
   1.518 + * DataSize upon which we operate, and the following eight functions.
   1.519 + *
   1.520 + * static Type add(Type* ptr, Type val);
   1.521 + * static Type sub(Type* ptr, Type val);
   1.522 + * static Type or_(Type* ptr, Type val);
   1.523 + * static Type xor_(Type* ptr, Type val);
   1.524 + * static Type and_(Type* ptr, Type val);
   1.525 + *
   1.526 + *   These functions perform the obvious operation on the value contained in
   1.527 + *   |*ptr| combined with |val| and return the value previously stored in
   1.528 + *   |*ptr|.
   1.529 + *
   1.530 + * static void store(Type* ptr, Type val);
   1.531 + *
   1.532 + *   This function atomically stores |val| into |*ptr| and must provide a full
   1.533 + *   memory fence after the store to prevent compiler and hardware instruction
   1.534 + *   reordering.  It should also act as a compiler barrier to prevent reads and
   1.535 + *   writes from moving to after the store.
   1.536 + *
   1.537 + * static Type exchange(Type* ptr, Type val);
   1.538 + *
   1.539 + *   This function atomically stores |val| into |*ptr| and returns the previous
   1.540 + *   contents of *ptr;
   1.541 + *
   1.542 + * static bool compareExchange(Type* ptr, Type oldVal, Type newVal);
   1.543 + *
   1.544 + *   This function atomically performs the following operation:
   1.545 + *
   1.546 + *     if (*ptr == oldVal) {
   1.547 + *       *ptr = newVal;
   1.548 + *       return true;
   1.549 + *     } else {
   1.550 + *       return false;
   1.551 + *     }
   1.552 + *
   1.553 + */
   1.554 +template<size_t DataSize> struct PrimitiveIntrinsics;
   1.555 +
   1.556 +template<>
   1.557 +struct PrimitiveIntrinsics<4>
   1.558 +{
   1.559 +    typedef long Type;
   1.560 +
   1.561 +    static Type add(Type* ptr, Type val) {
   1.562 +      return _InterlockedExchangeAdd(ptr, val);
   1.563 +    }
   1.564 +    static Type sub(Type* ptr, Type val) {
   1.565 +      /*
   1.566 +       * _InterlockedExchangeSubtract isn't available before Windows 7,
   1.567 +       * and we must support Windows XP.
   1.568 +       */
   1.569 +      return _InterlockedExchangeAdd(ptr, -val);
   1.570 +    }
   1.571 +    static Type or_(Type* ptr, Type val) {
   1.572 +      return _InterlockedOr(ptr, val);
   1.573 +    }
   1.574 +    static Type xor_(Type* ptr, Type val) {
   1.575 +      return _InterlockedXor(ptr, val);
   1.576 +    }
   1.577 +    static Type and_(Type* ptr, Type val) {
   1.578 +      return _InterlockedAnd(ptr, val);
   1.579 +    }
   1.580 +    static void store(Type* ptr, Type val) {
   1.581 +      _InterlockedExchange(ptr, val);
   1.582 +    }
   1.583 +    static Type exchange(Type* ptr, Type val) {
   1.584 +      return _InterlockedExchange(ptr, val);
   1.585 +    }
   1.586 +    static bool compareExchange(Type* ptr, Type oldVal, Type newVal) {
   1.587 +      return _InterlockedCompareExchange(ptr, newVal, oldVal) == oldVal;
   1.588 +    }
   1.589 +};
   1.590 +
   1.591 +#  if defined(_M_X64)
   1.592 +
   1.593 +extern "C" {
   1.594 +long long __cdecl _InterlockedExchangeAdd64(long long volatile* dst,
   1.595 +                                            long long value);
   1.596 +long long __cdecl _InterlockedOr64(long long volatile* dst,
   1.597 +                                   long long value);
   1.598 +long long __cdecl _InterlockedXor64(long long volatile* dst,
   1.599 +                                    long long value);
   1.600 +long long __cdecl _InterlockedAnd64(long long volatile* dst,
   1.601 +                                    long long value);
   1.602 +long long __cdecl _InterlockedExchange64(long long volatile* dst,
   1.603 +                                         long long value);
   1.604 +long long __cdecl _InterlockedCompareExchange64(long long volatile* dst,
   1.605 +                                                long long newVal,
   1.606 +                                                long long oldVal);
   1.607 +}
   1.608 +
   1.609 +#    pragma intrinsic(_InterlockedExchangeAdd64)
   1.610 +#    pragma intrinsic(_InterlockedOr64)
   1.611 +#    pragma intrinsic(_InterlockedXor64)
   1.612 +#    pragma intrinsic(_InterlockedAnd64)
   1.613 +#    pragma intrinsic(_InterlockedExchange64)
   1.614 +#    pragma intrinsic(_InterlockedCompareExchange64)
   1.615 +
   1.616 +template <>
   1.617 +struct PrimitiveIntrinsics<8>
   1.618 +{
   1.619 +    typedef __int64 Type;
   1.620 +
   1.621 +    static Type add(Type* ptr, Type val) {
   1.622 +      return _InterlockedExchangeAdd64(ptr, val);
   1.623 +    }
   1.624 +    static Type sub(Type* ptr, Type val) {
   1.625 +      /*
   1.626 +       * There is no _InterlockedExchangeSubtract64.
   1.627 +       */
   1.628 +      return _InterlockedExchangeAdd64(ptr, -val);
   1.629 +    }
   1.630 +    static Type or_(Type* ptr, Type val) {
   1.631 +      return _InterlockedOr64(ptr, val);
   1.632 +    }
   1.633 +    static Type xor_(Type* ptr, Type val) {
   1.634 +      return _InterlockedXor64(ptr, val);
   1.635 +    }
   1.636 +    static Type and_(Type* ptr, Type val) {
   1.637 +      return _InterlockedAnd64(ptr, val);
   1.638 +    }
   1.639 +    static void store(Type* ptr, Type val) {
   1.640 +      _InterlockedExchange64(ptr, val);
   1.641 +    }
   1.642 +    static Type exchange(Type* ptr, Type val) {
   1.643 +      return _InterlockedExchange64(ptr, val);
   1.644 +    }
   1.645 +    static bool compareExchange(Type* ptr, Type oldVal, Type newVal) {
   1.646 +      return _InterlockedCompareExchange64(ptr, newVal, oldVal) == oldVal;
   1.647 +    }
   1.648 +};
   1.649 +
   1.650 +#  endif
   1.651 +
   1.652 +extern "C" { void _ReadWriteBarrier(); }
   1.653 +
   1.654 +#  pragma intrinsic(_ReadWriteBarrier)
   1.655 +
   1.656 +template<MemoryOrdering Order> struct Barrier;
   1.657 +
   1.658 +/*
   1.659 + * We do not provide an afterStore method in Barrier, as Relaxed and
   1.660 + * ReleaseAcquire orderings do not require one, and the required barrier
   1.661 + * for SequentiallyConsistent is handled by PrimitiveIntrinsics.
   1.662 + */
   1.663 +
   1.664 +template<>
   1.665 +struct Barrier<Relaxed>
   1.666 +{
   1.667 +    static void beforeLoad() {}
   1.668 +    static void afterLoad() {}
   1.669 +    static void beforeStore() {}
   1.670 +};
   1.671 +
   1.672 +template<>
   1.673 +struct Barrier<ReleaseAcquire>
   1.674 +{
   1.675 +    static void beforeLoad() {}
   1.676 +    static void afterLoad() { _ReadWriteBarrier(); }
   1.677 +    static void beforeStore() { _ReadWriteBarrier(); }
   1.678 +};
   1.679 +
   1.680 +template<>
   1.681 +struct Barrier<SequentiallyConsistent>
   1.682 +{
   1.683 +    static void beforeLoad() { _ReadWriteBarrier(); }
   1.684 +    static void afterLoad() { _ReadWriteBarrier(); }
   1.685 +    static void beforeStore() { _ReadWriteBarrier(); }
   1.686 +};
   1.687 +
   1.688 +template<typename PrimType, typename T>
   1.689 +struct CastHelper
   1.690 +{
   1.691 +  static PrimType toPrimType(T val) { return static_cast<PrimType>(val); }
   1.692 +  static T fromPrimType(PrimType val) { return static_cast<T>(val); }
   1.693 +};
   1.694 +
   1.695 +template<typename PrimType, typename T>
   1.696 +struct CastHelper<PrimType, T*>
   1.697 +{
   1.698 +  static PrimType toPrimType(T* val) { return reinterpret_cast<PrimType>(val); }
   1.699 +  static T* fromPrimType(PrimType val) { return reinterpret_cast<T*>(val); }
   1.700 +};
   1.701 +
   1.702 +template<typename T>
   1.703 +struct IntrinsicBase
   1.704 +{
   1.705 +    typedef T ValueType;
   1.706 +    typedef PrimitiveIntrinsics<sizeof(T)> Primitives;
   1.707 +    typedef typename Primitives::Type PrimType;
   1.708 +    static_assert(sizeof(PrimType) == sizeof(T),
   1.709 +                  "Selection of PrimitiveIntrinsics was wrong");
   1.710 +    typedef CastHelper<PrimType, T> Cast;
   1.711 +};
   1.712 +
   1.713 +template<typename T, MemoryOrdering Order>
   1.714 +struct IntrinsicMemoryOps : public IntrinsicBase<T>
   1.715 +{
   1.716 +    typedef typename IntrinsicBase<T>::ValueType ValueType;
   1.717 +    typedef typename IntrinsicBase<T>::Primitives Primitives;
   1.718 +    typedef typename IntrinsicBase<T>::PrimType PrimType;
   1.719 +    typedef typename IntrinsicBase<T>::Cast Cast;
   1.720 +    static ValueType load(const ValueType& ptr) {
   1.721 +      Barrier<Order>::beforeLoad();
   1.722 +      ValueType val = ptr;
   1.723 +      Barrier<Order>::afterLoad();
   1.724 +      return val;
   1.725 +    }
   1.726 +    static void store(ValueType& ptr, ValueType val) {
   1.727 +      // For SequentiallyConsistent, Primitives::store() will generate the
   1.728 +      // proper memory fence.  Everything else just needs a barrier before
   1.729 +      // the store.
   1.730 +      if (Order == SequentiallyConsistent) {
   1.731 +        Primitives::store(reinterpret_cast<PrimType*>(&ptr),
   1.732 +                          Cast::toPrimType(val));
   1.733 +      } else {
   1.734 +        Barrier<Order>::beforeStore();
   1.735 +        ptr = val;
   1.736 +      }
   1.737 +    }
   1.738 +    static ValueType exchange(ValueType& ptr, ValueType val) {
   1.739 +      PrimType oldval =
   1.740 +        Primitives::exchange(reinterpret_cast<PrimType*>(&ptr),
   1.741 +                             Cast::toPrimType(val));
   1.742 +      return Cast::fromPrimType(oldval);
   1.743 +    }
   1.744 +    static bool compareExchange(ValueType& ptr, ValueType oldVal, ValueType newVal) {
   1.745 +      return Primitives::compareExchange(reinterpret_cast<PrimType*>(&ptr),
   1.746 +                                         Cast::toPrimType(oldVal),
   1.747 +                                         Cast::toPrimType(newVal));
   1.748 +    }
   1.749 +};
   1.750 +
   1.751 +template<typename T>
   1.752 +struct IntrinsicApplyHelper : public IntrinsicBase<T>
   1.753 +{
   1.754 +    typedef typename IntrinsicBase<T>::ValueType ValueType;
   1.755 +    typedef typename IntrinsicBase<T>::PrimType PrimType;
   1.756 +    typedef typename IntrinsicBase<T>::Cast Cast;
   1.757 +    typedef PrimType (*BinaryOp)(PrimType*, PrimType);
   1.758 +    typedef PrimType (*UnaryOp)(PrimType*);
   1.759 +
   1.760 +    static ValueType applyBinaryFunction(BinaryOp op, ValueType& ptr,
   1.761 +                                         ValueType val) {
   1.762 +      PrimType* primTypePtr = reinterpret_cast<PrimType*>(&ptr);
   1.763 +      PrimType primTypeVal = Cast::toPrimType(val);
   1.764 +      return Cast::fromPrimType(op(primTypePtr, primTypeVal));
   1.765 +    }
   1.766 +
   1.767 +    static ValueType applyUnaryFunction(UnaryOp op, ValueType& ptr) {
   1.768 +      PrimType* primTypePtr = reinterpret_cast<PrimType*>(&ptr);
   1.769 +      return Cast::fromPrimType(op(primTypePtr));
   1.770 +    }
   1.771 +};
   1.772 +
   1.773 +template<typename T>
   1.774 +struct IntrinsicAddSub : public IntrinsicApplyHelper<T>
   1.775 +{
   1.776 +    typedef typename IntrinsicApplyHelper<T>::ValueType ValueType;
   1.777 +    typedef typename IntrinsicBase<T>::Primitives Primitives;
   1.778 +    static ValueType add(ValueType& ptr, ValueType val) {
   1.779 +      return applyBinaryFunction(&Primitives::add, ptr, val);
   1.780 +    }
   1.781 +    static ValueType sub(ValueType& ptr, ValueType val) {
   1.782 +      return applyBinaryFunction(&Primitives::sub, ptr, val);
   1.783 +    }
   1.784 +};
   1.785 +
   1.786 +template<typename T>
   1.787 +struct IntrinsicAddSub<T*> : public IntrinsicApplyHelper<T*>
   1.788 +{
   1.789 +    typedef typename IntrinsicApplyHelper<T*>::ValueType ValueType;
   1.790 +    static ValueType add(ValueType& ptr, ptrdiff_t amount) {
   1.791 +      return applyBinaryFunction(&Primitives::add, ptr,
   1.792 +                                 (ValueType)(amount * sizeof(ValueType)));
   1.793 +    }
   1.794 +    static ValueType sub(ValueType& ptr, ptrdiff_t amount) {
   1.795 +      return applyBinaryFunction(&Primitives::sub, ptr,
   1.796 +                                 (ValueType)(amount * sizeof(ValueType)));
   1.797 +    }
   1.798 +};
   1.799 +
   1.800 +template<typename T>
   1.801 +struct IntrinsicIncDec : public IntrinsicAddSub<T>
   1.802 +{
   1.803 +    typedef typename IntrinsicAddSub<T>::ValueType ValueType;
   1.804 +    static ValueType inc(ValueType& ptr) { return add(ptr, 1); }
   1.805 +    static ValueType dec(ValueType& ptr) { return sub(ptr, 1); }
   1.806 +};
   1.807 +
   1.808 +template<typename T, MemoryOrdering Order>
   1.809 +struct AtomicIntrinsics : public IntrinsicMemoryOps<T, Order>,
   1.810 +                          public IntrinsicIncDec<T>
   1.811 +{
   1.812 +    typedef typename IntrinsicIncDec<T>::ValueType ValueType;
   1.813 +    static ValueType or_(ValueType& ptr, T val) {
   1.814 +      return applyBinaryFunction(&Primitives::or_, ptr, val);
   1.815 +    }
   1.816 +    static ValueType xor_(ValueType& ptr, T val) {
   1.817 +      return applyBinaryFunction(&Primitives::xor_, ptr, val);
   1.818 +    }
   1.819 +    static ValueType and_(ValueType& ptr, T val) {
   1.820 +      return applyBinaryFunction(&Primitives::and_, ptr, val);
   1.821 +    }
   1.822 +};
   1.823 +
   1.824 +template<typename T, MemoryOrdering Order>
   1.825 +struct AtomicIntrinsics<T*, Order> : public IntrinsicMemoryOps<T*, Order>,
   1.826 +                                     public IntrinsicIncDec<T*>
   1.827 +{
   1.828 +    typedef typename IntrinsicMemoryOps<T*, Order>::ValueType ValueType;
   1.829 +};
   1.830 +
   1.831 +} // namespace detail
   1.832 +} // namespace mozilla
   1.833 +
   1.834 +#else
   1.835 +# error "Atomic compiler intrinsics are not supported on your platform"
   1.836 +#endif
   1.837 +
   1.838 +namespace mozilla {
   1.839 +
   1.840 +namespace detail {
   1.841 +
   1.842 +template<typename T, MemoryOrdering Order>
   1.843 +class AtomicBase
   1.844 +{
   1.845 +    // We only support 32-bit types on 32-bit Windows, which constrains our
   1.846 +    // implementation elsewhere.  But we support pointer-sized types everywhere.
   1.847 +    static_assert(sizeof(T) == 4 || (sizeof(uintptr_t) == 8 && sizeof(T) == 8),
   1.848 +                  "mozilla/Atomics.h only supports 32-bit and pointer-sized types");
   1.849 +
   1.850 +  protected:
   1.851 +    typedef typename detail::AtomicIntrinsics<T, Order> Intrinsics;
   1.852 +    typename Intrinsics::ValueType mValue;
   1.853 +
   1.854 +  public:
   1.855 +    MOZ_CONSTEXPR AtomicBase() : mValue() {}
   1.856 +    MOZ_CONSTEXPR AtomicBase(T aInit) : mValue(aInit) {}
   1.857 +
   1.858 +    // Note: we can't provide operator T() here because Atomic<bool> inherits
   1.859 +    // from AtomcBase with T=uint32_t and not T=bool. If we implemented
   1.860 +    // operator T() here, it would cause errors when comparing Atomic<bool> with
   1.861 +    // a regular bool.
   1.862 +
   1.863 +    T operator=(T aValue) {
   1.864 +      Intrinsics::store(mValue, aValue);
   1.865 +      return aValue;
   1.866 +    }
   1.867 +
   1.868 +    /**
   1.869 +     * Performs an atomic swap operation.  aValue is stored and the previous
   1.870 +     * value of this variable is returned.
   1.871 +     */
   1.872 +    T exchange(T aValue) {
   1.873 +      return Intrinsics::exchange(mValue, aValue);
   1.874 +    }
   1.875 +
   1.876 +    /**
   1.877 +     * Performs an atomic compare-and-swap operation and returns true if it
   1.878 +     * succeeded. This is equivalent to atomically doing
   1.879 +     *
   1.880 +     *   if (mValue == aOldValue) {
   1.881 +     *     mValue = aNewValue;
   1.882 +     *     return true;
   1.883 +     *   } else {
   1.884 +     *     return false;
   1.885 +     *   }
   1.886 +     */
   1.887 +    bool compareExchange(T aOldValue, T aNewValue) {
   1.888 +      return Intrinsics::compareExchange(mValue, aOldValue, aNewValue);
   1.889 +    }
   1.890 +
   1.891 +  private:
   1.892 +    template<MemoryOrdering AnyOrder>
   1.893 +    AtomicBase(const AtomicBase<T, AnyOrder>& aCopy) MOZ_DELETE;
   1.894 +};
   1.895 +
   1.896 +template<typename T, MemoryOrdering Order>
   1.897 +class AtomicBaseIncDec : public AtomicBase<T, Order>
   1.898 +{
   1.899 +    typedef typename detail::AtomicBase<T, Order> Base;
   1.900 +
   1.901 +  public:
   1.902 +    MOZ_CONSTEXPR AtomicBaseIncDec() : Base() {}
   1.903 +    MOZ_CONSTEXPR AtomicBaseIncDec(T aInit) : Base(aInit) {}
   1.904 +
   1.905 +    using Base::operator=;
   1.906 +
   1.907 +    operator T() const { return Base::Intrinsics::load(Base::mValue); }
   1.908 +    T operator++(int) { return Base::Intrinsics::inc(Base::mValue); }
   1.909 +    T operator--(int) { return Base::Intrinsics::dec(Base::mValue); }
   1.910 +    T operator++() { return Base::Intrinsics::inc(Base::mValue) + 1; }
   1.911 +    T operator--() { return Base::Intrinsics::dec(Base::mValue) - 1; }
   1.912 +
   1.913 +  private:
   1.914 +    template<MemoryOrdering AnyOrder>
   1.915 +    AtomicBaseIncDec(const AtomicBaseIncDec<T, AnyOrder>& aCopy) MOZ_DELETE;
   1.916 +};
   1.917 +
   1.918 +} // namespace detail
   1.919 +
   1.920 +/**
   1.921 + * A wrapper for a type that enforces that all memory accesses are atomic.
   1.922 + *
   1.923 + * In general, where a variable |T foo| exists, |Atomic<T> foo| can be used in
   1.924 + * its place.  Implementations for integral and pointer types are provided
   1.925 + * below.
   1.926 + *
   1.927 + * Atomic accesses are sequentially consistent by default.  You should
   1.928 + * use the default unless you are tall enough to ride the
   1.929 + * memory-ordering roller coaster (if you're not sure, you aren't) and
   1.930 + * you have a compelling reason to do otherwise.
   1.931 + *
   1.932 + * There is one exception to the case of atomic memory accesses: providing an
   1.933 + * initial value of the atomic value is not guaranteed to be atomic.  This is a
   1.934 + * deliberate design choice that enables static atomic variables to be declared
   1.935 + * without introducing extra static constructors.
   1.936 + */
   1.937 +template<typename T,
   1.938 +         MemoryOrdering Order = SequentiallyConsistent,
   1.939 +         typename Enable = void>
   1.940 +class Atomic;
   1.941 +
   1.942 +/**
   1.943 + * Atomic<T> implementation for integral types.
   1.944 + *
   1.945 + * In addition to atomic store and load operations, compound assignment and
   1.946 + * increment/decrement operators are implemented which perform the
   1.947 + * corresponding read-modify-write operation atomically.  Finally, an atomic
   1.948 + * swap method is provided.
   1.949 + */
   1.950 +template<typename T, MemoryOrdering Order>
   1.951 +class Atomic<T, Order, typename EnableIf<IsIntegral<T>::value && !IsSame<T, bool>::value>::Type>
   1.952 +  : public detail::AtomicBaseIncDec<T, Order>
   1.953 +{
   1.954 +    typedef typename detail::AtomicBaseIncDec<T, Order> Base;
   1.955 +
   1.956 +  public:
   1.957 +    MOZ_CONSTEXPR Atomic() : Base() {}
   1.958 +    MOZ_CONSTEXPR Atomic(T aInit) : Base(aInit) {}
   1.959 +
   1.960 +    using Base::operator=;
   1.961 +
   1.962 +    T operator+=(T delta) { return Base::Intrinsics::add(Base::mValue, delta) + delta; }
   1.963 +    T operator-=(T delta) { return Base::Intrinsics::sub(Base::mValue, delta) - delta; }
   1.964 +    T operator|=(T val) { return Base::Intrinsics::or_(Base::mValue, val) | val; }
   1.965 +    T operator^=(T val) { return Base::Intrinsics::xor_(Base::mValue, val) ^ val; }
   1.966 +    T operator&=(T val) { return Base::Intrinsics::and_(Base::mValue, val) & val; }
   1.967 +
   1.968 +  private:
   1.969 +    Atomic(Atomic<T, Order>& aOther) MOZ_DELETE;
   1.970 +};
   1.971 +
   1.972 +/**
   1.973 + * Atomic<T> implementation for pointer types.
   1.974 + *
   1.975 + * An atomic compare-and-swap primitive for pointer variables is provided, as
   1.976 + * are atomic increment and decement operators.  Also provided are the compound
   1.977 + * assignment operators for addition and subtraction. Atomic swap (via
   1.978 + * exchange()) is included as well.
   1.979 + */
   1.980 +template<typename T, MemoryOrdering Order>
   1.981 +class Atomic<T*, Order> : public detail::AtomicBaseIncDec<T*, Order>
   1.982 +{
   1.983 +    typedef typename detail::AtomicBaseIncDec<T*, Order> Base;
   1.984 +
   1.985 +  public:
   1.986 +    MOZ_CONSTEXPR Atomic() : Base() {}
   1.987 +    MOZ_CONSTEXPR Atomic(T* aInit) : Base(aInit) {}
   1.988 +
   1.989 +    using Base::operator=;
   1.990 +
   1.991 +    T* operator+=(ptrdiff_t delta) {
   1.992 +      return Base::Intrinsics::add(Base::mValue, delta) + delta;
   1.993 +    }
   1.994 +    T* operator-=(ptrdiff_t delta) {
   1.995 +      return Base::Intrinsics::sub(Base::mValue, delta) - delta;
   1.996 +    }
   1.997 +
   1.998 +  private:
   1.999 +    Atomic(Atomic<T*, Order>& aOther) MOZ_DELETE;
  1.1000 +};
  1.1001 +
  1.1002 +/**
  1.1003 + * Atomic<T> implementation for enum types.
  1.1004 + *
  1.1005 + * The atomic store and load operations and the atomic swap method is provided.
  1.1006 + */
  1.1007 +template<typename T, MemoryOrdering Order>
  1.1008 +class Atomic<T, Order, typename EnableIf<IsEnum<T>::value>::Type>
  1.1009 +  : public detail::AtomicBase<T, Order>
  1.1010 +{
  1.1011 +    typedef typename detail::AtomicBase<T, Order> Base;
  1.1012 +
  1.1013 +  public:
  1.1014 +    MOZ_CONSTEXPR Atomic() : Base() {}
  1.1015 +    MOZ_CONSTEXPR Atomic(T aInit) : Base(aInit) {}
  1.1016 +
  1.1017 +    operator T() const { return Base::Intrinsics::load(Base::mValue); }
  1.1018 +
  1.1019 +    using Base::operator=;
  1.1020 +
  1.1021 +  private:
  1.1022 +    Atomic(Atomic<T, Order>& aOther) MOZ_DELETE;
  1.1023 +};
  1.1024 +
  1.1025 +/**
  1.1026 + * Atomic<T> implementation for boolean types.
  1.1027 + *
  1.1028 + * The atomic store and load operations and the atomic swap method is provided.
  1.1029 + *
  1.1030 + * Note:
  1.1031 + *
  1.1032 + * - sizeof(Atomic<bool>) != sizeof(bool) for some implementations of
  1.1033 + *   bool and/or some implementations of std::atomic. This is allowed in
  1.1034 + *   [atomic.types.generic]p9.
  1.1035 + *
  1.1036 + * - It's not obvious whether the 8-bit atomic functions on Windows are always
  1.1037 + *   inlined or not. If they are not inlined, the corresponding functions in the
  1.1038 + *   runtime library are not available on Windows XP. This is why we implement
  1.1039 + *   Atomic<bool> with an underlying type of uint32_t.
  1.1040 + */
  1.1041 +template<MemoryOrdering Order>
  1.1042 +class Atomic<bool, Order>
  1.1043 +  : protected detail::AtomicBase<uint32_t, Order>
  1.1044 +{
  1.1045 +    typedef typename detail::AtomicBase<uint32_t, Order> Base;
  1.1046 +
  1.1047 +  public:
  1.1048 +    MOZ_CONSTEXPR Atomic() : Base() {}
  1.1049 +    MOZ_CONSTEXPR Atomic(bool aInit) : Base(aInit) {}
  1.1050 +
  1.1051 +    // We provide boolean wrappers for the underlying AtomicBase methods.
  1.1052 +    operator bool() const { return Base::Intrinsics::load(Base::mValue); }
  1.1053 +    bool operator=(bool aValue) { return Base::operator=(aValue); }
  1.1054 +    bool exchange(bool aValue) { return Base::exchange(aValue); }
  1.1055 +    bool compareExchange(bool aOldValue, bool aNewValue) {
  1.1056 +      return Base::compareExchange(aOldValue, aNewValue);
  1.1057 +    }
  1.1058 +
  1.1059 +  private:
  1.1060 +    Atomic(Atomic<bool, Order>& aOther) MOZ_DELETE;
  1.1061 +};
  1.1062 +
  1.1063 +} // namespace mozilla
  1.1064 +
  1.1065 +#endif /* mozilla_Atomics_h */

mercurial