diff --git a/mfbt/Atomics.h b/mfbt/Atomics.h index 94bf90044182..94bdf44241d3 100644 --- a/mfbt/Atomics.h +++ b/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 @@ -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 struct AutoRecordAtomicAccess; + +template<> +struct AutoRecordAtomicAccess { + AutoRecordAtomicAccess() {} + ~AutoRecordAtomicAccess() {} +}; + +template<> +struct AutoRecordAtomicAccess { + AutoRecordAtomicAccess() { recordreplay::BeginOrderedAtomicAccess(); } + ~AutoRecordAtomicAccess() { recordreplay::EndOrderedAtomicAccess(); } +}; + /* * We provide CompareExchangeFailureOrder to work around a bug in some * versions of GCC's header. See bug 898491. @@ -186,108 +211,120 @@ struct IntrinsicBase typedef AtomicOrderConstraints OrderedOp; }; -template +template struct IntrinsicMemoryOps : public IntrinsicBase { typedef IntrinsicBase Base; static T load(const typename Base::ValueType& aPtr) { + AutoRecordAtomicAccess record; return aPtr.load(Base::OrderedOp::LoadOrder); } static void store(typename Base::ValueType& aPtr, T aVal) { + AutoRecordAtomicAccess record; aPtr.store(aVal, Base::OrderedOp::StoreOrder); } static T exchange(typename Base::ValueType& aPtr, T aVal) { + AutoRecordAtomicAccess record; return aPtr.exchange(aVal, Base::OrderedOp::AtomicRMWOrder); } static bool compareExchange(typename Base::ValueType& aPtr, T aOldVal, T aNewVal) { + AutoRecordAtomicAccess record; return aPtr.compare_exchange_strong(aOldVal, aNewVal, Base::OrderedOp::AtomicRMWOrder, Base::OrderedOp::CompareExchangeFailureOrder); } }; -template +template struct IntrinsicAddSub : public IntrinsicBase { typedef IntrinsicBase Base; static T add(typename Base::ValueType& aPtr, T aVal) { + AutoRecordAtomicAccess record; return aPtr.fetch_add(aVal, Base::OrderedOp::AtomicRMWOrder); } static T sub(typename Base::ValueType& aPtr, T aVal) { + AutoRecordAtomicAccess record; return aPtr.fetch_sub(aVal, Base::OrderedOp::AtomicRMWOrder); } }; -template -struct IntrinsicAddSub : public IntrinsicBase +template +struct IntrinsicAddSub : public IntrinsicBase { typedef IntrinsicBase Base; static T* add(typename Base::ValueType& aPtr, ptrdiff_t aVal) { + AutoRecordAtomicAccess record; return aPtr.fetch_add(aVal, Base::OrderedOp::AtomicRMWOrder); } static T* sub(typename Base::ValueType& aPtr, ptrdiff_t aVal) { + AutoRecordAtomicAccess record; return aPtr.fetch_sub(aVal, Base::OrderedOp::AtomicRMWOrder); } }; -template -struct IntrinsicIncDec : public IntrinsicAddSub +template +struct IntrinsicIncDec : public IntrinsicAddSub { typedef IntrinsicBase Base; static T inc(typename Base::ValueType& aPtr) { - return IntrinsicAddSub::add(aPtr, 1); + return IntrinsicAddSub::add(aPtr, 1); } static T dec(typename Base::ValueType& aPtr) { - return IntrinsicAddSub::sub(aPtr, 1); + return IntrinsicAddSub::sub(aPtr, 1); } }; -template -struct AtomicIntrinsics : public IntrinsicMemoryOps, - public IntrinsicIncDec +template +struct AtomicIntrinsics : public IntrinsicMemoryOps, + public IntrinsicIncDec { typedef IntrinsicBase Base; static T or_(typename Base::ValueType& aPtr, T aVal) { + AutoRecordAtomicAccess record; return aPtr.fetch_or(aVal, Base::OrderedOp::AtomicRMWOrder); } static T xor_(typename Base::ValueType& aPtr, T aVal) { + AutoRecordAtomicAccess record; return aPtr.fetch_xor(aVal, Base::OrderedOp::AtomicRMWOrder); } static T and_(typename Base::ValueType& aPtr, T aVal) { + AutoRecordAtomicAccess record; return aPtr.fetch_and(aVal, Base::OrderedOp::AtomicRMWOrder); } }; -template -struct AtomicIntrinsics - : public IntrinsicMemoryOps, public IntrinsicIncDec +template +struct AtomicIntrinsics + : public IntrinsicMemoryOps, + public IntrinsicIncDec { }; @@ -297,14 +334,14 @@ struct ToStorageTypeArgument static constexpr T convert (T aT) { return aT; } }; -template +template 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 Intrinsics; + typedef typename detail::AtomicIntrinsics Intrinsics; typedef typename Intrinsics::ValueType ValueType; ValueType mValue; @@ -351,14 +388,13 @@ public: } private: - template - AtomicBase(const AtomicBase& aCopy) = delete; + AtomicBase(const AtomicBase& aCopy) = delete; }; -template -class AtomicBaseIncDec : public AtomicBase +template +class AtomicBaseIncDec : public AtomicBase { - typedef typename detail::AtomicBase Base; + typedef typename detail::AtomicBase Base; public: constexpr AtomicBaseIncDec() : Base() {} @@ -373,8 +409,7 @@ public: T operator--() { return Base::Intrinsics::dec(Base::mValue) - 1; } private: - template - AtomicBaseIncDec(const AtomicBaseIncDec& aCopy) = delete; + AtomicBaseIncDec(const AtomicBaseIncDec& aCopy) = delete; }; } // namespace detail @@ -398,6 +433,7 @@ private: */ template class Atomic; @@ -409,12 +445,13 @@ class Atomic; * corresponding read-modify-write operation atomically. Finally, an atomic * swap method is provided. */ -template -class Atomic::value && - !IsSame::value>::Type> - : public detail::AtomicBaseIncDec +template +class Atomic::value && + !IsSame::value>::Type> + : public detail::AtomicBaseIncDec { - typedef typename detail::AtomicBaseIncDec Base; + typedef typename detail::AtomicBaseIncDec Base; public: constexpr Atomic() : Base() {} @@ -448,7 +485,7 @@ public: } private: - Atomic(Atomic& 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 -class Atomic : public detail::AtomicBaseIncDec +template +class Atomic : public detail::AtomicBaseIncDec { - typedef typename detail::AtomicBaseIncDec Base; + typedef typename detail::AtomicBaseIncDec Base; public: constexpr Atomic() : Base() {} @@ -481,7 +518,7 @@ public: } private: - Atomic(Atomic& 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 -class Atomic::value>::Type> - : public detail::AtomicBase +template +class Atomic::value>::Type> + : public detail::AtomicBase { - typedef typename detail::AtomicBase Base; + typedef typename detail::AtomicBase Base; public: constexpr Atomic() : Base() {} @@ -504,7 +541,7 @@ public: using Base::operator=; private: - Atomic(Atomic& 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 with an underlying type of uint32_t. */ -template -class Atomic - : protected detail::AtomicBase +template +class Atomic + : protected detail::AtomicBase { - typedef typename detail::AtomicBase Base; + typedef typename detail::AtomicBase Base; public: constexpr Atomic() : Base() {} @@ -555,7 +592,7 @@ public: } private: - Atomic(Atomic& aOther) = delete; + Atomic(Atomic& aOther) = delete; }; // If you want to atomically swap two atomic values, use exchange(). diff --git a/mfbt/RecordReplay.cpp b/mfbt/RecordReplay.cpp index 9800e3e544fa..80b5bf140ab3 100644 --- a/mfbt/RecordReplay.cpp +++ b/mfbt/RecordReplay.cpp @@ -6,6 +6,8 @@ #include "RecordReplay.h" +#include "js/GCAnnotations.h" +#include "mozilla/Atomics.h" #include "mozilla/Casting.h" #include @@ -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 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; \ } diff --git a/mfbt/RecordReplay.h b/mfbt/RecordReplay.h index 69c7304ca0fe..14543d92b196 100644 --- a/mfbt/RecordReplay.h +++ b/mfbt/RecordReplay.h @@ -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, (), ())