Bug 1207696 Part 2a - Atomics interface changes, r=waldo.

--HG--
extra : rebase_source : 39c1910c273357bdacbea7c15d9c49eccd2aeb2c
This commit is contained in:
Brian Hackett 2018-07-21 14:17:16 +00:00
parent d024714803
commit 47ccde0dda
3 changed files with 114 additions and 43 deletions

View File

@ -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().

View File

@ -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; \
}

View File

@ -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, (), ())