mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1220693 - Make mozilla::Atomic<enum class> work even on compilers that don't have <atomic>. r=froydnj
This commit is contained in:
parent
7e5d43cd58
commit
5000852e29
101
mfbt/Atomics.h
101
mfbt/Atomics.h
@ -320,6 +320,12 @@ struct AtomicIntrinsics<T*, Order>
|
||||
{
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct ToStorageTypeArgument
|
||||
{
|
||||
static T convert (T aT) { return aT; }
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace mozilla
|
||||
|
||||
@ -384,60 +390,81 @@ struct Barrier<SequentiallyConsistent>
|
||||
static void afterStore() { __sync_synchronize(); }
|
||||
};
|
||||
|
||||
template<typename T, bool TIsEnum = IsEnum<T>::value>
|
||||
struct AtomicStorageType
|
||||
{
|
||||
// For non-enums, just use the type directly.
|
||||
typedef T Type;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct AtomicStorageType<T, true>
|
||||
: Conditional<sizeof(T) == 4, uint32_t, uint64_t>
|
||||
{
|
||||
static_assert(sizeof(T) == 4 || sizeof(T) == 8,
|
||||
"wrong type computed in conditional above");
|
||||
};
|
||||
|
||||
template<typename T, MemoryOrdering Order>
|
||||
struct IntrinsicMemoryOps
|
||||
{
|
||||
static T load(const T& aPtr)
|
||||
typedef typename AtomicStorageType<T>::Type ValueType;
|
||||
|
||||
static T load(const ValueType& aPtr)
|
||||
{
|
||||
Barrier<Order>::beforeLoad();
|
||||
T val = aPtr;
|
||||
T val = T(aPtr);
|
||||
Barrier<Order>::afterLoad();
|
||||
return val;
|
||||
}
|
||||
|
||||
static void store(T& aPtr, T aVal)
|
||||
static void store(ValueType& aPtr, T aVal)
|
||||
{
|
||||
Barrier<Order>::beforeStore();
|
||||
aPtr = aVal;
|
||||
aPtr = ValueType(aVal);
|
||||
Barrier<Order>::afterStore();
|
||||
}
|
||||
|
||||
static T exchange(T& aPtr, T aVal)
|
||||
static T exchange(ValueType& aPtr, T aVal)
|
||||
{
|
||||
// __sync_lock_test_and_set is only an acquire barrier; loads and stores
|
||||
// can't be moved up from after to before it, but they can be moved down
|
||||
// from before to after it. We may want a stricter ordering, so we need
|
||||
// an explicit barrier.
|
||||
Barrier<Order>::beforeStore();
|
||||
return __sync_lock_test_and_set(&aPtr, aVal);
|
||||
return T(__sync_lock_test_and_set(&aPtr, ValueType(aVal)));
|
||||
}
|
||||
|
||||
static bool compareExchange(T& aPtr, T aOldVal, T aNewVal)
|
||||
static bool compareExchange(ValueType& aPtr, T aOldVal, T aNewVal)
|
||||
{
|
||||
return __sync_bool_compare_and_swap(&aPtr, aOldVal, aNewVal);
|
||||
return __sync_bool_compare_and_swap(&aPtr, ValueType(aOldVal), ValueType(aNewVal));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
template<typename T, MemoryOrdering Order>
|
||||
struct IntrinsicAddSub
|
||||
: public IntrinsicMemoryOps<T, Order>
|
||||
{
|
||||
typedef T ValueType;
|
||||
typedef IntrinsicMemoryOps<T, Order> Base;
|
||||
typedef typename Base::ValueType ValueType;
|
||||
|
||||
static T add(T& aPtr, T aVal)
|
||||
static T add(ValueType& aPtr, T aVal)
|
||||
{
|
||||
return __sync_fetch_and_add(&aPtr, aVal);
|
||||
return T(__sync_fetch_and_add(&aPtr, ValueType(aVal)));
|
||||
}
|
||||
|
||||
static T sub(T& aPtr, T aVal)
|
||||
static T sub(ValueType& aPtr, T aVal)
|
||||
{
|
||||
return __sync_fetch_and_sub(&aPtr, aVal);
|
||||
return T(__sync_fetch_and_sub(&aPtr, ValueType(aVal)));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct IntrinsicAddSub<T*>
|
||||
template<typename T, MemoryOrdering Order>
|
||||
struct IntrinsicAddSub<T*, Order>
|
||||
: public IntrinsicMemoryOps<T*, Order>
|
||||
{
|
||||
typedef T* ValueType;
|
||||
typedef IntrinsicMemoryOps<T*, Order> Base;
|
||||
typedef typename Base::ValueType ValueType;
|
||||
|
||||
/*
|
||||
* The reinterpret_casts are needed so that
|
||||
@ -459,16 +486,18 @@ struct IntrinsicAddSub<T*>
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct IntrinsicIncDec : public IntrinsicAddSub<T>
|
||||
template<typename T, MemoryOrdering Order>
|
||||
struct IntrinsicIncDec : public IntrinsicAddSub<T, Order>
|
||||
{
|
||||
static T inc(T& aPtr) { return IntrinsicAddSub<T>::add(aPtr, 1); }
|
||||
static T dec(T& aPtr) { return IntrinsicAddSub<T>::sub(aPtr, 1); }
|
||||
typedef IntrinsicAddSub<T, Order> Base;
|
||||
typedef typename Base::ValueType ValueType;
|
||||
|
||||
static T inc(ValueType& aPtr) { return Base::add(aPtr, 1); }
|
||||
static T dec(ValueType& aPtr) { return Base::sub(aPtr, 1); }
|
||||
};
|
||||
|
||||
template<typename T, MemoryOrdering Order>
|
||||
struct AtomicIntrinsics : public IntrinsicMemoryOps<T, Order>,
|
||||
public IntrinsicIncDec<T>
|
||||
struct AtomicIntrinsics : public IntrinsicIncDec<T, Order>
|
||||
{
|
||||
static T or_( T& aPtr, T aVal) { return __sync_fetch_and_or(&aPtr, aVal); }
|
||||
static T xor_(T& aPtr, T aVal) { return __sync_fetch_and_xor(&aPtr, aVal); }
|
||||
@ -476,11 +505,24 @@ struct AtomicIntrinsics : public IntrinsicMemoryOps<T, Order>,
|
||||
};
|
||||
|
||||
template<typename T, MemoryOrdering Order>
|
||||
struct AtomicIntrinsics<T*, Order> : public IntrinsicMemoryOps<T*, Order>,
|
||||
public IntrinsicIncDec<T*>
|
||||
struct AtomicIntrinsics<T*, Order> : public IntrinsicIncDec<T*, Order>
|
||||
{
|
||||
};
|
||||
|
||||
template<typename T, bool TIsEnum = IsEnum<T>::value>
|
||||
struct ToStorageTypeArgument
|
||||
{
|
||||
typedef typename AtomicStorageType<T>::Type ResultType;
|
||||
|
||||
static ResultType convert (T aT) { return ResultType(aT); }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct ToStorageTypeArgument<T, false>
|
||||
{
|
||||
static T convert (T aT) { return aT; }
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace mozilla
|
||||
|
||||
@ -500,11 +542,14 @@ class AtomicBase
|
||||
|
||||
protected:
|
||||
typedef typename detail::AtomicIntrinsics<T, Order> Intrinsics;
|
||||
typename Intrinsics::ValueType mValue;
|
||||
typedef typename Intrinsics::ValueType ValueType;
|
||||
ValueType mValue;
|
||||
|
||||
public:
|
||||
MOZ_CONSTEXPR AtomicBase() : mValue() {}
|
||||
explicit MOZ_CONSTEXPR AtomicBase(T aInit) : mValue(aInit) {}
|
||||
explicit MOZ_CONSTEXPR AtomicBase(T aInit)
|
||||
: mValue(ToStorageTypeArgument<T>::convert(aInit))
|
||||
{}
|
||||
|
||||
// Note: we can't provide operator T() here because Atomic<bool> inherits
|
||||
// from AtomcBase with T=uint32_t and not T=bool. If we implemented
|
||||
@ -691,7 +736,7 @@ public:
|
||||
MOZ_CONSTEXPR Atomic() : Base() {}
|
||||
explicit MOZ_CONSTEXPR Atomic(T aInit) : Base(aInit) {}
|
||||
|
||||
operator T() const { return Base::Intrinsics::load(Base::mValue); }
|
||||
operator T() const { return T(Base::Intrinsics::load(Base::mValue)); }
|
||||
|
||||
using Base::operator=;
|
||||
|
||||
|
@ -168,6 +168,44 @@ TestEnumWithOrdering()
|
||||
A(atomic == EnumType_3, "CAS should have changed atomic's value.");
|
||||
}
|
||||
|
||||
enum class EnumClass : uint32_t
|
||||
{
|
||||
Value0 = 0,
|
||||
Value1 = 1,
|
||||
Value2 = 2,
|
||||
Value3 = 3
|
||||
};
|
||||
|
||||
template<MemoryOrdering Order>
|
||||
static void
|
||||
TestEnumClassWithOrdering()
|
||||
{
|
||||
Atomic<EnumClass, Order> atomic(EnumClass::Value2);
|
||||
A(atomic == EnumClass::Value2, "Atomic variable did not initialize");
|
||||
|
||||
// Test assignment
|
||||
EnumClass result;
|
||||
result = (atomic = EnumClass::Value3);
|
||||
A(atomic == EnumClass::Value3, "Atomic assignment failed");
|
||||
A(result == EnumClass::Value3, "Atomic assignment returned the wrong value");
|
||||
|
||||
// Test exchange.
|
||||
atomic = EnumClass::Value1;
|
||||
result = atomic.exchange(EnumClass::Value2);
|
||||
A(atomic == EnumClass::Value2, "Atomic exchange did not work");
|
||||
A(result == EnumClass::Value1, "Atomic exchange returned the wrong value");
|
||||
|
||||
// Test CAS.
|
||||
atomic = EnumClass::Value1;
|
||||
bool boolResult = atomic.compareExchange(EnumClass::Value0, EnumClass::Value2);
|
||||
A(!boolResult, "CAS should have returned false.");
|
||||
A(atomic == EnumClass::Value1, "CAS shouldn't have done anything.");
|
||||
|
||||
boolResult = atomic.compareExchange(EnumClass::Value1, EnumClass::Value3);
|
||||
A(boolResult, "CAS should have succeeded.");
|
||||
A(atomic == EnumClass::Value3, "CAS should have changed atomic's value.");
|
||||
}
|
||||
|
||||
template <MemoryOrdering Order>
|
||||
static void
|
||||
TestBoolWithOrdering()
|
||||
@ -222,6 +260,10 @@ TestEnum()
|
||||
TestEnumWithOrdering<SequentiallyConsistent>();
|
||||
TestEnumWithOrdering<ReleaseAcquire>();
|
||||
TestEnumWithOrdering<Relaxed>();
|
||||
|
||||
TestEnumClassWithOrdering<SequentiallyConsistent>();
|
||||
TestEnumClassWithOrdering<ReleaseAcquire>();
|
||||
TestEnumClassWithOrdering<Relaxed>();
|
||||
}
|
||||
|
||||
static void
|
||||
|
Loading…
Reference in New Issue
Block a user