mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-27 23:02:20 +00:00
Bug 1207696 Part 2a - Atomics interface changes, r=waldo.
--HG-- extra : rebase_source : 39c1910c273357bdacbea7c15d9c49eccd2aeb2c
This commit is contained in:
parent
d024714803
commit
47ccde0dda
123
mfbt/Atomics.h
123
mfbt/Atomics.h
@ -19,6 +19,7 @@
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Compiler.h"
|
||||
#include "mozilla/RecordReplay.h"
|
||||
#include "mozilla/TypeTraits.h"
|
||||
|
||||
#include <atomic>
|
||||
@ -143,6 +144,30 @@ enum MemoryOrdering {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/*
|
||||
* Structure which can be used to preserve the ordering of atomic accesses
|
||||
* when recording or replaying an execution, depending on the Recording enum.
|
||||
*
|
||||
* Atomic access ordering is preserved by default when recording/replaying.
|
||||
* This should be overridden for atomics that can be accessed in code that
|
||||
* runs non-deterministically when recording/replaying, such as during GC, the
|
||||
* JS interrupt callback, or code that is affected by JIT compilation or
|
||||
* debugger activity.
|
||||
*/
|
||||
template<recordreplay::Behavior Recording> struct AutoRecordAtomicAccess;
|
||||
|
||||
template<>
|
||||
struct AutoRecordAtomicAccess<recordreplay::Behavior::DontPreserve> {
|
||||
AutoRecordAtomicAccess() {}
|
||||
~AutoRecordAtomicAccess() {}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct AutoRecordAtomicAccess<recordreplay::Behavior::Preserve> {
|
||||
AutoRecordAtomicAccess() { recordreplay::BeginOrderedAtomicAccess(); }
|
||||
~AutoRecordAtomicAccess() { recordreplay::EndOrderedAtomicAccess(); }
|
||||
};
|
||||
|
||||
/*
|
||||
* We provide CompareExchangeFailureOrder to work around a bug in some
|
||||
* versions of GCC's <atomic> header. See bug 898491.
|
||||
@ -186,108 +211,120 @@ struct IntrinsicBase
|
||||
typedef AtomicOrderConstraints<Order> OrderedOp;
|
||||
};
|
||||
|
||||
template<typename T, MemoryOrdering Order>
|
||||
template<typename T, MemoryOrdering Order, recordreplay::Behavior Recording>
|
||||
struct IntrinsicMemoryOps : public IntrinsicBase<T, Order>
|
||||
{
|
||||
typedef IntrinsicBase<T, Order> Base;
|
||||
|
||||
static T load(const typename Base::ValueType& aPtr)
|
||||
{
|
||||
AutoRecordAtomicAccess<Recording> record;
|
||||
return aPtr.load(Base::OrderedOp::LoadOrder);
|
||||
}
|
||||
|
||||
static void store(typename Base::ValueType& aPtr, T aVal)
|
||||
{
|
||||
AutoRecordAtomicAccess<Recording> record;
|
||||
aPtr.store(aVal, Base::OrderedOp::StoreOrder);
|
||||
}
|
||||
|
||||
static T exchange(typename Base::ValueType& aPtr, T aVal)
|
||||
{
|
||||
AutoRecordAtomicAccess<Recording> record;
|
||||
return aPtr.exchange(aVal, Base::OrderedOp::AtomicRMWOrder);
|
||||
}
|
||||
|
||||
static bool compareExchange(typename Base::ValueType& aPtr,
|
||||
T aOldVal, T aNewVal)
|
||||
{
|
||||
AutoRecordAtomicAccess<Recording> record;
|
||||
return aPtr.compare_exchange_strong(aOldVal, aNewVal,
|
||||
Base::OrderedOp::AtomicRMWOrder,
|
||||
Base::OrderedOp::CompareExchangeFailureOrder);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, MemoryOrdering Order>
|
||||
template<typename T, MemoryOrdering Order, recordreplay::Behavior Recording>
|
||||
struct IntrinsicAddSub : public IntrinsicBase<T, Order>
|
||||
{
|
||||
typedef IntrinsicBase<T, Order> Base;
|
||||
|
||||
static T add(typename Base::ValueType& aPtr, T aVal)
|
||||
{
|
||||
AutoRecordAtomicAccess<Recording> record;
|
||||
return aPtr.fetch_add(aVal, Base::OrderedOp::AtomicRMWOrder);
|
||||
}
|
||||
|
||||
static T sub(typename Base::ValueType& aPtr, T aVal)
|
||||
{
|
||||
AutoRecordAtomicAccess<Recording> record;
|
||||
return aPtr.fetch_sub(aVal, Base::OrderedOp::AtomicRMWOrder);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, MemoryOrdering Order>
|
||||
struct IntrinsicAddSub<T*, Order> : public IntrinsicBase<T*, Order>
|
||||
template<typename T, MemoryOrdering Order, recordreplay::Behavior Recording>
|
||||
struct IntrinsicAddSub<T*, Order, Recording> : public IntrinsicBase<T*, Order>
|
||||
{
|
||||
typedef IntrinsicBase<T*, Order> Base;
|
||||
|
||||
static T* add(typename Base::ValueType& aPtr, ptrdiff_t aVal)
|
||||
{
|
||||
AutoRecordAtomicAccess<Recording> record;
|
||||
return aPtr.fetch_add(aVal, Base::OrderedOp::AtomicRMWOrder);
|
||||
}
|
||||
|
||||
static T* sub(typename Base::ValueType& aPtr, ptrdiff_t aVal)
|
||||
{
|
||||
AutoRecordAtomicAccess<Recording> record;
|
||||
return aPtr.fetch_sub(aVal, Base::OrderedOp::AtomicRMWOrder);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, MemoryOrdering Order>
|
||||
struct IntrinsicIncDec : public IntrinsicAddSub<T, Order>
|
||||
template<typename T, MemoryOrdering Order, recordreplay::Behavior Recording>
|
||||
struct IntrinsicIncDec : public IntrinsicAddSub<T, Order, Recording>
|
||||
{
|
||||
typedef IntrinsicBase<T, Order> Base;
|
||||
|
||||
static T inc(typename Base::ValueType& aPtr)
|
||||
{
|
||||
return IntrinsicAddSub<T, Order>::add(aPtr, 1);
|
||||
return IntrinsicAddSub<T, Order, Recording>::add(aPtr, 1);
|
||||
}
|
||||
|
||||
static T dec(typename Base::ValueType& aPtr)
|
||||
{
|
||||
return IntrinsicAddSub<T, Order>::sub(aPtr, 1);
|
||||
return IntrinsicAddSub<T, Order, Recording>::sub(aPtr, 1);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, MemoryOrdering Order>
|
||||
struct AtomicIntrinsics : public IntrinsicMemoryOps<T, Order>,
|
||||
public IntrinsicIncDec<T, Order>
|
||||
template<typename T, MemoryOrdering Order, recordreplay::Behavior Recording>
|
||||
struct AtomicIntrinsics : public IntrinsicMemoryOps<T, Order, Recording>,
|
||||
public IntrinsicIncDec<T, Order, Recording>
|
||||
{
|
||||
typedef IntrinsicBase<T, Order> Base;
|
||||
|
||||
static T or_(typename Base::ValueType& aPtr, T aVal)
|
||||
{
|
||||
AutoRecordAtomicAccess<Recording> record;
|
||||
return aPtr.fetch_or(aVal, Base::OrderedOp::AtomicRMWOrder);
|
||||
}
|
||||
|
||||
static T xor_(typename Base::ValueType& aPtr, T aVal)
|
||||
{
|
||||
AutoRecordAtomicAccess<Recording> record;
|
||||
return aPtr.fetch_xor(aVal, Base::OrderedOp::AtomicRMWOrder);
|
||||
}
|
||||
|
||||
static T and_(typename Base::ValueType& aPtr, T aVal)
|
||||
{
|
||||
AutoRecordAtomicAccess<Recording> record;
|
||||
return aPtr.fetch_and(aVal, Base::OrderedOp::AtomicRMWOrder);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, MemoryOrdering Order>
|
||||
struct AtomicIntrinsics<T*, Order>
|
||||
: public IntrinsicMemoryOps<T*, Order>, public IntrinsicIncDec<T*, Order>
|
||||
template<typename T, MemoryOrdering Order, recordreplay::Behavior Recording>
|
||||
struct AtomicIntrinsics<T*, Order, Recording>
|
||||
: public IntrinsicMemoryOps<T*, Order, Recording>,
|
||||
public IntrinsicIncDec<T*, Order, Recording>
|
||||
{
|
||||
};
|
||||
|
||||
@ -297,14 +334,14 @@ struct ToStorageTypeArgument
|
||||
static constexpr T convert (T aT) { return aT; }
|
||||
};
|
||||
|
||||
template<typename T, MemoryOrdering Order>
|
||||
template<typename T, MemoryOrdering Order, recordreplay::Behavior Recording>
|
||||
class AtomicBase
|
||||
{
|
||||
static_assert(sizeof(T) == 4 || sizeof(T) == 8,
|
||||
"mozilla/Atomics.h only supports 32-bit and 64-bit types");
|
||||
|
||||
protected:
|
||||
typedef typename detail::AtomicIntrinsics<T, Order> Intrinsics;
|
||||
typedef typename detail::AtomicIntrinsics<T, Order, Recording> Intrinsics;
|
||||
typedef typename Intrinsics::ValueType ValueType;
|
||||
ValueType mValue;
|
||||
|
||||
@ -351,14 +388,13 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
template<MemoryOrdering AnyOrder>
|
||||
AtomicBase(const AtomicBase<T, AnyOrder>& aCopy) = delete;
|
||||
AtomicBase(const AtomicBase& aCopy) = delete;
|
||||
};
|
||||
|
||||
template<typename T, MemoryOrdering Order>
|
||||
class AtomicBaseIncDec : public AtomicBase<T, Order>
|
||||
template<typename T, MemoryOrdering Order, recordreplay::Behavior Recording>
|
||||
class AtomicBaseIncDec : public AtomicBase<T, Order, Recording>
|
||||
{
|
||||
typedef typename detail::AtomicBase<T, Order> Base;
|
||||
typedef typename detail::AtomicBase<T, Order, Recording> Base;
|
||||
|
||||
public:
|
||||
constexpr AtomicBaseIncDec() : Base() {}
|
||||
@ -373,8 +409,7 @@ public:
|
||||
T operator--() { return Base::Intrinsics::dec(Base::mValue) - 1; }
|
||||
|
||||
private:
|
||||
template<MemoryOrdering AnyOrder>
|
||||
AtomicBaseIncDec(const AtomicBaseIncDec<T, AnyOrder>& aCopy) = delete;
|
||||
AtomicBaseIncDec(const AtomicBaseIncDec& aCopy) = delete;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
@ -398,6 +433,7 @@ private:
|
||||
*/
|
||||
template<typename T,
|
||||
MemoryOrdering Order = SequentiallyConsistent,
|
||||
recordreplay::Behavior Recording = recordreplay::Behavior::Preserve,
|
||||
typename Enable = void>
|
||||
class Atomic;
|
||||
|
||||
@ -409,12 +445,13 @@ class Atomic;
|
||||
* corresponding read-modify-write operation atomically. Finally, an atomic
|
||||
* swap method is provided.
|
||||
*/
|
||||
template<typename T, MemoryOrdering Order>
|
||||
class Atomic<T, Order, typename EnableIf<IsIntegral<T>::value &&
|
||||
!IsSame<T, bool>::value>::Type>
|
||||
: public detail::AtomicBaseIncDec<T, Order>
|
||||
template<typename T, MemoryOrdering Order, recordreplay::Behavior Recording>
|
||||
class Atomic<T, Order, Recording,
|
||||
typename EnableIf<IsIntegral<T>::value &&
|
||||
!IsSame<T, bool>::value>::Type>
|
||||
: public detail::AtomicBaseIncDec<T, Order, Recording>
|
||||
{
|
||||
typedef typename detail::AtomicBaseIncDec<T, Order> Base;
|
||||
typedef typename detail::AtomicBaseIncDec<T, Order, Recording> Base;
|
||||
|
||||
public:
|
||||
constexpr Atomic() : Base() {}
|
||||
@ -448,7 +485,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
Atomic(Atomic<T, Order>& aOther) = delete;
|
||||
Atomic(Atomic& aOther) = delete;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -459,10 +496,10 @@ private:
|
||||
* assignment operators for addition and subtraction. Atomic swap (via
|
||||
* exchange()) is included as well.
|
||||
*/
|
||||
template<typename T, MemoryOrdering Order>
|
||||
class Atomic<T*, Order> : public detail::AtomicBaseIncDec<T*, Order>
|
||||
template<typename T, MemoryOrdering Order, recordreplay::Behavior Recording>
|
||||
class Atomic<T*, Order, Recording> : public detail::AtomicBaseIncDec<T*, Order, Recording>
|
||||
{
|
||||
typedef typename detail::AtomicBaseIncDec<T*, Order> Base;
|
||||
typedef typename detail::AtomicBaseIncDec<T*, Order, Recording> Base;
|
||||
|
||||
public:
|
||||
constexpr Atomic() : Base() {}
|
||||
@ -481,7 +518,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
Atomic(Atomic<T*, Order>& aOther) = delete;
|
||||
Atomic(Atomic& aOther) = delete;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -489,11 +526,11 @@ private:
|
||||
*
|
||||
* The atomic store and load operations and the atomic swap method is provided.
|
||||
*/
|
||||
template<typename T, MemoryOrdering Order>
|
||||
class Atomic<T, Order, typename EnableIf<IsEnum<T>::value>::Type>
|
||||
: public detail::AtomicBase<T, Order>
|
||||
template<typename T, MemoryOrdering Order, recordreplay::Behavior Recording>
|
||||
class Atomic<T, Order, Recording, typename EnableIf<IsEnum<T>::value>::Type>
|
||||
: public detail::AtomicBase<T, Order, Recording>
|
||||
{
|
||||
typedef typename detail::AtomicBase<T, Order> Base;
|
||||
typedef typename detail::AtomicBase<T, Order, Recording> Base;
|
||||
|
||||
public:
|
||||
constexpr Atomic() : Base() {}
|
||||
@ -504,7 +541,7 @@ public:
|
||||
using Base::operator=;
|
||||
|
||||
private:
|
||||
Atomic(Atomic<T, Order>& aOther) = delete;
|
||||
Atomic(Atomic& aOther) = delete;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -523,11 +560,11 @@ private:
|
||||
* runtime library are not available on Windows XP. This is why we implement
|
||||
* Atomic<bool> with an underlying type of uint32_t.
|
||||
*/
|
||||
template<MemoryOrdering Order>
|
||||
class Atomic<bool, Order>
|
||||
: protected detail::AtomicBase<uint32_t, Order>
|
||||
template<MemoryOrdering Order, recordreplay::Behavior Recording>
|
||||
class Atomic<bool, Order, Recording>
|
||||
: protected detail::AtomicBase<uint32_t, Order, Recording>
|
||||
{
|
||||
typedef typename detail::AtomicBase<uint32_t, Order> Base;
|
||||
typedef typename detail::AtomicBase<uint32_t, Order, Recording> Base;
|
||||
|
||||
public:
|
||||
constexpr Atomic() : Base() {}
|
||||
@ -555,7 +592,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
Atomic(Atomic<bool, Order>& aOther) = delete;
|
||||
Atomic(Atomic& aOther) = delete;
|
||||
};
|
||||
|
||||
// If you want to atomically swap two atomic values, use exchange().
|
||||
|
@ -6,6 +6,8 @@
|
||||
|
||||
#include "RecordReplay.h"
|
||||
|
||||
#include "js/GCAnnotations.h"
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/Casting.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
@ -139,9 +141,24 @@ FOR_EACH_INTERFACE_VOID(INIT_SYMBOL_VOID)
|
||||
initialize(aArgc, aArgv);
|
||||
}
|
||||
|
||||
// Record/replay API functions can't GC, but we can't use
|
||||
// JS::AutoSuppressGCAnalysis here due to linking issues.
|
||||
struct AutoSuppressGCAnalysis
|
||||
{
|
||||
AutoSuppressGCAnalysis() {}
|
||||
~AutoSuppressGCAnalysis() {
|
||||
#ifdef DEBUG
|
||||
// Need nontrivial destructor.
|
||||
static Atomic<int, SequentiallyConsistent, Behavior::DontPreserve> dummy;
|
||||
dummy++;
|
||||
#endif
|
||||
}
|
||||
} JS_HAZ_GC_SUPPRESSED;
|
||||
|
||||
#define DEFINE_WRAPPER(aName, aReturnType, aFormals, aActuals) \
|
||||
aReturnType aName aFormals \
|
||||
{ \
|
||||
AutoSuppressGCAnalysis suppress; \
|
||||
MOZ_ASSERT(IsRecordingOrReplaying() || IsMiddleman()); \
|
||||
return gPtr ##aName aActuals; \
|
||||
}
|
||||
@ -149,6 +166,7 @@ FOR_EACH_INTERFACE_VOID(INIT_SYMBOL_VOID)
|
||||
#define DEFINE_WRAPPER_VOID(aName, aFormals, aActuals) \
|
||||
void aName aFormals \
|
||||
{ \
|
||||
AutoSuppressGCAnalysis suppress; \
|
||||
MOZ_ASSERT(IsRecordingOrReplaying() || IsMiddleman()); \
|
||||
gPtr ##aName aActuals; \
|
||||
}
|
||||
|
@ -482,6 +482,9 @@ public:
|
||||
// API inline function implementation
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Define inline wrappers on builds where recording/replaying is enabled.
|
||||
#if defined(XP_MACOSX) && defined(NIGHTLY_BUILD)
|
||||
|
||||
#define MOZ_MakeRecordReplayWrapperVoid(aName, aFormals, aActuals) \
|
||||
MFBT_API void Internal ##aName aFormals; \
|
||||
static inline void aName aFormals \
|
||||
@ -501,6 +504,19 @@ public:
|
||||
return aDefaultValue; \
|
||||
}
|
||||
|
||||
// Define inline wrappers on other builds. Avoiding references to the out of
|
||||
// line method avoids link errors when e.g. using Atomic<> but not linking
|
||||
// against MFBT.
|
||||
#else
|
||||
|
||||
#define MOZ_MakeRecordReplayWrapperVoid(aName, aFormals, aActuals) \
|
||||
static inline void aName aFormals {}
|
||||
|
||||
#define MOZ_MakeRecordReplayWrapper(aName, aReturnType, aDefaultValue, aFormals, aActuals) \
|
||||
static inline aReturnType aName aFormals { return aDefaultValue; }
|
||||
|
||||
#endif
|
||||
|
||||
MOZ_MakeRecordReplayWrapperVoid(BeginOrderedAtomicAccess, (), ())
|
||||
MOZ_MakeRecordReplayWrapperVoid(EndOrderedAtomicAccess, (), ())
|
||||
MOZ_MakeRecordReplayWrapperVoid(BeginPassThroughThreadEvents, (), ())
|
||||
|
Loading…
Reference in New Issue
Block a user