LCOV - code coverage report
Current view: top level - mfbt - Atomics.h (source / functions) Hit Total Coverage
Test: output.info Lines: 56 60 93.3 %
Date: 2017-07-14 16:53:18 Functions: 269 393 68.4 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : /*
       8             :  * Implements (almost always) lock-free atomic operations. The operations here
       9             :  * are a subset of that which can be found in C++11's <atomic> header, with a
      10             :  * different API to enforce consistent memory ordering constraints.
      11             :  *
      12             :  * Anyone caught using |volatile| for inter-thread memory safety needs to be
      13             :  * sent a copy of this header and the C++11 standard.
      14             :  */
      15             : 
      16             : #ifndef mozilla_Atomics_h
      17             : #define mozilla_Atomics_h
      18             : 
      19             : #include "mozilla/Assertions.h"
      20             : #include "mozilla/Attributes.h"
      21             : #include "mozilla/Compiler.h"
      22             : #include "mozilla/TypeTraits.h"
      23             : 
      24             : #include <atomic>
      25             : 
      26             : #include <stdint.h>
      27             : 
      28             : namespace mozilla {
      29             : 
      30             : /**
      31             :  * An enum of memory ordering possibilities for atomics.
      32             :  *
      33             :  * Memory ordering is the observable state of distinct values in memory.
      34             :  * (It's a separate concept from atomicity, which concerns whether an
      35             :  * operation can ever be observed in an intermediate state.  Don't
      36             :  * conflate the two!)  Given a sequence of operations in source code on
      37             :  * memory, it is *not* always the case that, at all times and on all
      38             :  * cores, those operations will appear to have occurred in that exact
      39             :  * sequence.  First, the compiler might reorder that sequence, if it
      40             :  * thinks another ordering will be more efficient.  Second, the CPU may
      41             :  * not expose so consistent a view of memory.  CPUs will often perform
      42             :  * their own instruction reordering, above and beyond that performed by
      43             :  * the compiler.  And each core has its own memory caches, and accesses
      44             :  * (reads and writes both) to "memory" may only resolve to out-of-date
      45             :  * cache entries -- not to the "most recently" performed operation in
      46             :  * some global sense.  Any access to a value that may be used by
      47             :  * multiple threads, potentially across multiple cores, must therefore
      48             :  * have a memory ordering imposed on it, for all code on all
      49             :  * threads/cores to have a sufficiently coherent worldview.
      50             :  *
      51             :  * http://gcc.gnu.org/wiki/Atomic/GCCMM/AtomicSync and
      52             :  * http://en.cppreference.com/w/cpp/atomic/memory_order go into more
      53             :  * detail on all this, including examples of how each mode works.
      54             :  *
      55             :  * Note that for simplicity and practicality, not all of the modes in
      56             :  * C++11 are supported.  The missing C++11 modes are either subsumed by
      57             :  * the modes we provide below, or not relevant for the CPUs we support
      58             :  * in Gecko.  These three modes are confusing enough as it is!
      59             :  */
      60             : enum MemoryOrdering {
      61             :   /*
      62             :    * Relaxed ordering is the simplest memory ordering: none at all.
      63             :    * When the result of a write is observed, nothing may be inferred
      64             :    * about other memory.  Writes ostensibly performed "before" on the
      65             :    * writing thread may not yet be visible.  Writes performed "after" on
      66             :    * the writing thread may already be visible, if the compiler or CPU
      67             :    * reordered them.  (The latter can happen if reads and/or writes get
      68             :    * held up in per-processor caches.)  Relaxed ordering means
      69             :    * operations can always use cached values (as long as the actual
      70             :    * updates to atomic values actually occur, correctly, eventually), so
      71             :    * it's usually the fastest sort of atomic access.  For this reason,
      72             :    * *it's also the most dangerous kind of access*.
      73             :    *
      74             :    * Relaxed ordering is good for things like process-wide statistics
      75             :    * counters that don't need to be consistent with anything else, so
      76             :    * long as updates themselves are atomic.  (And so long as any
      77             :    * observations of that value can tolerate being out-of-date -- if you
      78             :    * need some sort of up-to-date value, you need some sort of other
      79             :    * synchronizing operation.)  It's *not* good for locks, mutexes,
      80             :    * reference counts, etc. that mediate access to other memory, or must
      81             :    * be observably consistent with other memory.
      82             :    *
      83             :    * x86 architectures don't take advantage of the optimization
      84             :    * opportunities that relaxed ordering permits.  Thus it's possible
      85             :    * that using relaxed ordering will "work" on x86 but fail elsewhere
      86             :    * (ARM, say, which *does* implement non-sequentially-consistent
      87             :    * relaxed ordering semantics).  Be extra-careful using relaxed
      88             :    * ordering if you can't easily test non-x86 architectures!
      89             :    */
      90             :   Relaxed,
      91             : 
      92             :   /*
      93             :    * When an atomic value is updated with ReleaseAcquire ordering, and
      94             :    * that new value is observed with ReleaseAcquire ordering, prior
      95             :    * writes (atomic or not) are also observable.  What ReleaseAcquire
      96             :    * *doesn't* give you is any observable ordering guarantees for
      97             :    * ReleaseAcquire-ordered operations on different objects.  For
      98             :    * example, if there are two cores that each perform ReleaseAcquire
      99             :    * operations on separate objects, each core may or may not observe
     100             :    * the operations made by the other core.  The only way the cores can
     101             :    * be synchronized with ReleaseAcquire is if they both
     102             :    * ReleaseAcquire-access the same object.  This implies that you can't
     103             :    * necessarily describe some global total ordering of ReleaseAcquire
     104             :    * operations.
     105             :    *
     106             :    * ReleaseAcquire ordering is good for (as the name implies) atomic
     107             :    * operations on values controlling ownership of things: reference
     108             :    * counts, mutexes, and the like.  However, if you are thinking about
     109             :    * using these to implement your own locks or mutexes, you should take
     110             :    * a good, hard look at actual lock or mutex primitives first.
     111             :    */
     112             :   ReleaseAcquire,
     113             : 
     114             :   /*
     115             :    * When an atomic value is updated with SequentiallyConsistent
     116             :    * ordering, all writes observable when the update is observed, just
     117             :    * as with ReleaseAcquire ordering.  But, furthermore, a global total
     118             :    * ordering of SequentiallyConsistent operations *can* be described.
     119             :    * For example, if two cores perform SequentiallyConsistent operations
     120             :    * on separate objects, one core will observably perform its update
     121             :    * (and all previous operations will have completed), then the other
     122             :    * core will observably perform its update (and all previous
     123             :    * operations will have completed).  (Although those previous
     124             :    * operations aren't themselves ordered -- they could be intermixed,
     125             :    * or ordered if they occur on atomic values with ordering
     126             :    * requirements.)  SequentiallyConsistent is the *simplest and safest*
     127             :    * ordering of atomic operations -- it's always as if one operation
     128             :    * happens, then another, then another, in some order -- and every
     129             :    * core observes updates to happen in that single order.  Because it
     130             :    * has the most synchronization requirements, operations ordered this
     131             :    * way also tend to be slowest.
     132             :    *
     133             :    * SequentiallyConsistent ordering can be desirable when multiple
     134             :    * threads observe objects, and they all have to agree on the
     135             :    * observable order of changes to them.  People expect
     136             :    * SequentiallyConsistent ordering, even if they shouldn't, when
     137             :    * writing code, atomic or otherwise.  SequentiallyConsistent is also
     138             :    * the ordering of choice when designing lockless data structures.  If
     139             :    * you don't know what order to use, use this one.
     140             :    */
     141             :   SequentiallyConsistent,
     142             : };
     143             : 
     144             : namespace detail {
     145             : 
     146             : /*
     147             :  * We provide CompareExchangeFailureOrder to work around a bug in some
     148             :  * versions of GCC's <atomic> header.  See bug 898491.
     149             :  */
     150             : template<MemoryOrdering Order> struct AtomicOrderConstraints;
     151             : 
     152             : template<>
     153             : struct AtomicOrderConstraints<Relaxed>
     154             : {
     155             :   static const std::memory_order AtomicRMWOrder = std::memory_order_relaxed;
     156             :   static const std::memory_order LoadOrder = std::memory_order_relaxed;
     157             :   static const std::memory_order StoreOrder = std::memory_order_relaxed;
     158             :   static const std::memory_order CompareExchangeFailureOrder =
     159             :     std::memory_order_relaxed;
     160             : };
     161             : 
     162             : template<>
     163             : struct AtomicOrderConstraints<ReleaseAcquire>
     164             : {
     165             :   static const std::memory_order AtomicRMWOrder = std::memory_order_acq_rel;
     166             :   static const std::memory_order LoadOrder = std::memory_order_acquire;
     167             :   static const std::memory_order StoreOrder = std::memory_order_release;
     168             :   static const std::memory_order CompareExchangeFailureOrder =
     169             :     std::memory_order_acquire;
     170             : };
     171             : 
     172             : template<>
     173             : struct AtomicOrderConstraints<SequentiallyConsistent>
     174             : {
     175             :   static const std::memory_order AtomicRMWOrder = std::memory_order_seq_cst;
     176             :   static const std::memory_order LoadOrder = std::memory_order_seq_cst;
     177             :   static const std::memory_order StoreOrder = std::memory_order_seq_cst;
     178             :   static const std::memory_order CompareExchangeFailureOrder =
     179             :     std::memory_order_seq_cst;
     180             : };
     181             : 
     182             : template<typename T, MemoryOrdering Order>
     183             : struct IntrinsicBase
     184             : {
     185             :   typedef std::atomic<T> ValueType;
     186             :   typedef AtomicOrderConstraints<Order> OrderedOp;
     187             : };
     188             : 
     189             : template<typename T, MemoryOrdering Order>
     190             : struct IntrinsicMemoryOps : public IntrinsicBase<T, Order>
     191             : {
     192             :   typedef IntrinsicBase<T, Order> Base;
     193             : 
     194   166737112 :   static T load(const typename Base::ValueType& aPtr)
     195             :   {
     196   284177328 :     return aPtr.load(Base::OrderedOp::LoadOrder);
     197             :   }
     198             : 
     199       97119 :   static void store(typename Base::ValueType& aPtr, T aVal)
     200             :   {
     201       97119 :     aPtr.store(aVal, Base::OrderedOp::StoreOrder);
     202       97118 :   }
     203             : 
     204      436304 :   static T exchange(typename Base::ValueType& aPtr, T aVal)
     205             :   {
     206      872607 :     return aPtr.exchange(aVal, Base::OrderedOp::AtomicRMWOrder);
     207             :   }
     208             : 
     209         144 :   static bool compareExchange(typename Base::ValueType& aPtr,
     210             :                               T aOldVal, T aNewVal)
     211             :   {
     212             :     return aPtr.compare_exchange_strong(aOldVal, aNewVal,
     213             :                                         Base::OrderedOp::AtomicRMWOrder,
     214         247 :                                         Base::OrderedOp::CompareExchangeFailureOrder);
     215             :   }
     216             : };
     217             : 
     218             : template<typename T, MemoryOrdering Order>
     219             : struct IntrinsicAddSub : public IntrinsicBase<T, Order>
     220             : {
     221             :   typedef IntrinsicBase<T, Order> Base;
     222             : 
     223      645083 :   static T add(typename Base::ValueType& aPtr, T aVal)
     224             :   {
     225     1290166 :     return aPtr.fetch_add(aVal, Base::OrderedOp::AtomicRMWOrder);
     226             :   }
     227             : 
     228      531640 :   static T sub(typename Base::ValueType& aPtr, T aVal)
     229             :   {
     230     1063280 :     return aPtr.fetch_sub(aVal, Base::OrderedOp::AtomicRMWOrder);
     231             :   }
     232             : };
     233             : 
     234             : template<typename T, MemoryOrdering Order>
     235             : struct IntrinsicAddSub<T*, Order> : public IntrinsicBase<T*, Order>
     236             : {
     237             :   typedef IntrinsicBase<T*, Order> Base;
     238             : 
     239             :   static T* add(typename Base::ValueType& aPtr, ptrdiff_t aVal)
     240             :   {
     241             :     return aPtr.fetch_add(aVal, Base::OrderedOp::AtomicRMWOrder);
     242             :   }
     243             : 
     244             :   static T* sub(typename Base::ValueType& aPtr, ptrdiff_t aVal)
     245             :   {
     246             :     return aPtr.fetch_sub(aVal, Base::OrderedOp::AtomicRMWOrder);
     247             :   }
     248             : };
     249             : 
     250             : template<typename T, MemoryOrdering Order>
     251             : struct IntrinsicIncDec : public IntrinsicAddSub<T, Order>
     252             : {
     253             :   typedef IntrinsicBase<T, Order> Base;
     254             : 
     255      615983 :   static T inc(typename Base::ValueType& aPtr)
     256             :   {
     257      615983 :     return IntrinsicAddSub<T, Order>::add(aPtr, 1);
     258             :   }
     259             : 
     260      318542 :   static T dec(typename Base::ValueType& aPtr)
     261             :   {
     262      318542 :     return IntrinsicAddSub<T, Order>::sub(aPtr, 1);
     263             :   }
     264             : };
     265             : 
     266             : template<typename T, MemoryOrdering Order>
     267             : struct AtomicIntrinsics : public IntrinsicMemoryOps<T, Order>,
     268             :                           public IntrinsicIncDec<T, Order>
     269             : {
     270             :   typedef IntrinsicBase<T, Order> Base;
     271             : 
     272           0 :   static T or_(typename Base::ValueType& aPtr, T aVal)
     273             :   {
     274           0 :     return aPtr.fetch_or(aVal, Base::OrderedOp::AtomicRMWOrder);
     275             :   }
     276             : 
     277             :   static T xor_(typename Base::ValueType& aPtr, T aVal)
     278             :   {
     279             :     return aPtr.fetch_xor(aVal, Base::OrderedOp::AtomicRMWOrder);
     280             :   }
     281             : 
     282             :   static T and_(typename Base::ValueType& aPtr, T aVal)
     283             :   {
     284             :     return aPtr.fetch_and(aVal, Base::OrderedOp::AtomicRMWOrder);
     285             :   }
     286             : };
     287             : 
     288             : template<typename T, MemoryOrdering Order>
     289             : struct AtomicIntrinsics<T*, Order>
     290             :   : public IntrinsicMemoryOps<T*, Order>, public IntrinsicIncDec<T*, Order>
     291             : {
     292             : };
     293             : 
     294             : template<typename T>
     295             : struct ToStorageTypeArgument
     296             : {
     297       49687 :   static constexpr T convert (T aT) { return aT; }
     298             : };
     299             : 
     300             : template<typename T, MemoryOrdering Order>
     301             : class AtomicBase
     302             : {
     303             :   static_assert(sizeof(T) == 4 || sizeof(T) == 8,
     304             :                 "mozilla/Atomics.h only supports 32-bit and 64-bit types");
     305             : 
     306             : protected:
     307             :   typedef typename detail::AtomicIntrinsics<T, Order> Intrinsics;
     308             :   typedef typename Intrinsics::ValueType ValueType;
     309             :   ValueType mValue;
     310             : 
     311             : public:
     312         183 :   constexpr AtomicBase() : mValue() {}
     313       49687 :   explicit constexpr AtomicBase(T aInit)
     314       49687 :     : mValue(ToStorageTypeArgument<T>::convert(aInit))
     315       49687 :   {}
     316             : 
     317             :   // Note: we can't provide operator T() here because Atomic<bool> inherits
     318             :   // from AtomcBase with T=uint32_t and not T=bool. If we implemented
     319             :   // operator T() here, it would cause errors when comparing Atomic<bool> with
     320             :   // a regular bool.
     321             : 
     322       97120 :   T operator=(T aVal)
     323             :   {
     324       97120 :     Intrinsics::store(mValue, aVal);
     325       97121 :     return aVal;
     326             :   }
     327             : 
     328             :   /**
     329             :    * Performs an atomic swap operation.  aVal is stored and the previous
     330             :    * value of this variable is returned.
     331             :    */
     332      436296 :   T exchange(T aVal)
     333             :   {
     334      436296 :     return Intrinsics::exchange(mValue, aVal);
     335             :   }
     336             : 
     337             :   /**
     338             :    * Performs an atomic compare-and-swap operation and returns true if it
     339             :    * succeeded. This is equivalent to atomically doing
     340             :    *
     341             :    *   if (mValue == aOldValue) {
     342             :    *     mValue = aNewValue;
     343             :    *     return true;
     344             :    *   } else {
     345             :    *     return false;
     346             :    *   }
     347             :    */
     348         144 :   bool compareExchange(T aOldValue, T aNewValue)
     349             :   {
     350         144 :     return Intrinsics::compareExchange(mValue, aOldValue, aNewValue);
     351             :   }
     352             : 
     353             : private:
     354             :   template<MemoryOrdering AnyOrder>
     355             :   AtomicBase(const AtomicBase<T, AnyOrder>& aCopy) = delete;
     356             : };
     357             : 
     358             : template<typename T, MemoryOrdering Order>
     359             : class AtomicBaseIncDec : public AtomicBase<T, Order>
     360             : {
     361             :   typedef typename detail::AtomicBase<T, Order> Base;
     362             : 
     363             : public:
     364         131 :   constexpr AtomicBaseIncDec() : Base() {}
     365       43848 :   explicit constexpr AtomicBaseIncDec(T aInit) : Base(aInit) {}
     366             : 
     367             :   using Base::operator=;
     368             : 
     369   160084244 :   operator T() const { return Base::Intrinsics::load(Base::mValue); }
     370      585240 :   T operator++(int) { return Base::Intrinsics::inc(Base::mValue); }
     371      299274 :   T operator--(int) { return Base::Intrinsics::dec(Base::mValue); }
     372       30740 :   T operator++() { return Base::Intrinsics::inc(Base::mValue) + 1; }
     373       19268 :   T operator--() { return Base::Intrinsics::dec(Base::mValue) - 1; }
     374             : 
     375             : private:
     376             :   template<MemoryOrdering AnyOrder>
     377             :   AtomicBaseIncDec(const AtomicBaseIncDec<T, AnyOrder>& aCopy) = delete;
     378             : };
     379             : 
     380             : } // namespace detail
     381             : 
     382             : /**
     383             :  * A wrapper for a type that enforces that all memory accesses are atomic.
     384             :  *
     385             :  * In general, where a variable |T foo| exists, |Atomic<T> foo| can be used in
     386             :  * its place.  Implementations for integral and pointer types are provided
     387             :  * below.
     388             :  *
     389             :  * Atomic accesses are sequentially consistent by default.  You should
     390             :  * use the default unless you are tall enough to ride the
     391             :  * memory-ordering roller coaster (if you're not sure, you aren't) and
     392             :  * you have a compelling reason to do otherwise.
     393             :  *
     394             :  * There is one exception to the case of atomic memory accesses: providing an
     395             :  * initial value of the atomic value is not guaranteed to be atomic.  This is a
     396             :  * deliberate design choice that enables static atomic variables to be declared
     397             :  * without introducing extra static constructors.
     398             :  */
     399             : template<typename T,
     400             :          MemoryOrdering Order = SequentiallyConsistent,
     401             :          typename Enable = void>
     402             : class Atomic;
     403             : 
     404             : /**
     405             :  * Atomic<T> implementation for integral types.
     406             :  *
     407             :  * In addition to atomic store and load operations, compound assignment and
     408             :  * increment/decrement operators are implemented which perform the
     409             :  * corresponding read-modify-write operation atomically.  Finally, an atomic
     410             :  * swap method is provided.
     411             :  */
     412             : template<typename T, MemoryOrdering Order>
     413             : class Atomic<T, Order, typename EnableIf<IsIntegral<T>::value &&
     414             :                        !IsSame<T, bool>::value>::Type>
     415             :   : public detail::AtomicBaseIncDec<T, Order>
     416             : {
     417             :   typedef typename detail::AtomicBaseIncDec<T, Order> Base;
     418             : 
     419             : public:
     420          11 :   constexpr Atomic() : Base() {}
     421       28485 :   explicit constexpr Atomic(T aInit) : Base(aInit) {}
     422             : 
     423             :   using Base::operator=;
     424             : 
     425       29063 :   T operator+=(T aDelta)
     426             :   {
     427       29063 :     return Base::Intrinsics::add(Base::mValue, aDelta) + aDelta;
     428             :   }
     429             : 
     430      213064 :   T operator-=(T aDelta)
     431             :   {
     432      213064 :     return Base::Intrinsics::sub(Base::mValue, aDelta) - aDelta;
     433             :   }
     434             : 
     435           0 :   T operator|=(T aVal)
     436             :   {
     437           0 :     return Base::Intrinsics::or_(Base::mValue, aVal) | aVal;
     438             :   }
     439             : 
     440             :   T operator^=(T aVal)
     441             :   {
     442             :     return Base::Intrinsics::xor_(Base::mValue, aVal) ^ aVal;
     443             :   }
     444             : 
     445             :   T operator&=(T aVal)
     446             :   {
     447             :     return Base::Intrinsics::and_(Base::mValue, aVal) & aVal;
     448             :   }
     449             : 
     450             : private:
     451             :   Atomic(Atomic<T, Order>& aOther) = delete;
     452             : };
     453             : 
     454             : /**
     455             :  * Atomic<T> implementation for pointer types.
     456             :  *
     457             :  * An atomic compare-and-swap primitive for pointer variables is provided, as
     458             :  * are atomic increment and decement operators.  Also provided are the compound
     459             :  * assignment operators for addition and subtraction. Atomic swap (via
     460             :  * exchange()) is included as well.
     461             :  */
     462             : template<typename T, MemoryOrdering Order>
     463             : class Atomic<T*, Order> : public detail::AtomicBaseIncDec<T*, Order>
     464             : {
     465             :   typedef typename detail::AtomicBaseIncDec<T*, Order> Base;
     466             : 
     467             : public:
     468         120 :   constexpr Atomic() : Base() {}
     469       15363 :   explicit constexpr Atomic(T* aInit) : Base(aInit) {}
     470             : 
     471             :   using Base::operator=;
     472             : 
     473             :   T* operator+=(ptrdiff_t aDelta)
     474             :   {
     475             :     return Base::Intrinsics::add(Base::mValue, aDelta) + aDelta;
     476             :   }
     477             : 
     478             :   T* operator-=(ptrdiff_t aDelta)
     479             :   {
     480             :     return Base::Intrinsics::sub(Base::mValue, aDelta) - aDelta;
     481             :   }
     482             : 
     483             : private:
     484             :   Atomic(Atomic<T*, Order>& aOther) = delete;
     485             : };
     486             : 
     487             : /**
     488             :  * Atomic<T> implementation for enum types.
     489             :  *
     490             :  * The atomic store and load operations and the atomic swap method is provided.
     491             :  */
     492             : template<typename T, MemoryOrdering Order>
     493             : class Atomic<T, Order, typename EnableIf<IsEnum<T>::value>::Type>
     494             :   : public detail::AtomicBase<T, Order>
     495             : {
     496             :   typedef typename detail::AtomicBase<T, Order> Base;
     497             : 
     498             : public:
     499             :   constexpr Atomic() : Base() {}
     500         205 :   explicit constexpr Atomic(T aInit) : Base(aInit) {}
     501             : 
     502      149907 :   operator T() const { return T(Base::Intrinsics::load(Base::mValue)); }
     503             : 
     504             :   using Base::operator=;
     505             : 
     506             : private:
     507             :   Atomic(Atomic<T, Order>& aOther) = delete;
     508             : };
     509             : 
     510             : /**
     511             :  * Atomic<T> implementation for boolean types.
     512             :  *
     513             :  * The atomic store and load operations and the atomic swap method is provided.
     514             :  *
     515             :  * Note:
     516             :  *
     517             :  * - sizeof(Atomic<bool>) != sizeof(bool) for some implementations of
     518             :  *   bool and/or some implementations of std::atomic. This is allowed in
     519             :  *   [atomic.types.generic]p9.
     520             :  *
     521             :  * - It's not obvious whether the 8-bit atomic functions on Windows are always
     522             :  *   inlined or not. If they are not inlined, the corresponding functions in the
     523             :  *   runtime library are not available on Windows XP. This is why we implement
     524             :  *   Atomic<bool> with an underlying type of uint32_t.
     525             :  */
     526             : template<MemoryOrdering Order>
     527             : class Atomic<bool, Order>
     528             :   : protected detail::AtomicBase<uint32_t, Order>
     529             : {
     530             :   typedef typename detail::AtomicBase<uint32_t, Order> Base;
     531             : 
     532             : public:
     533          52 :   constexpr Atomic() : Base() {}
     534        5634 :   explicit constexpr Atomic(bool aInit) : Base(aInit) {}
     535             : 
     536             :   // We provide boolean wrappers for the underlying AtomicBase methods.
     537     6517304 :   MOZ_IMPLICIT operator bool() const
     538             :   {
     539     6517304 :     return Base::Intrinsics::load(Base::mValue);
     540             :   }
     541             : 
     542       25041 :   bool operator=(bool aVal)
     543             :   {
     544       25041 :     return Base::operator=(aVal);
     545             :   }
     546             : 
     547        1389 :   bool exchange(bool aVal)
     548             :   {
     549        1389 :     return Base::exchange(aVal);
     550             :   }
     551             : 
     552         103 :   bool compareExchange(bool aOldValue, bool aNewValue)
     553             :   {
     554         103 :     return Base::compareExchange(aOldValue, aNewValue);
     555             :   }
     556             : 
     557             : private:
     558             :   Atomic(Atomic<bool, Order>& aOther) = delete;
     559             : };
     560             : 
     561             : } // namespace mozilla
     562             : 
     563             : #endif /* mozilla_Atomics_h */

Generated by: LCOV version 1.13