Use acq_rel ordering semantics in qatomic_cxx11.h ref/deref

Change ref() and deref() in qatomic_cxx11.h to use acq_rel ordered
semantics as documented instead of the stricter sequential consistency
that was previously used implicitly.

It was pointed out in the discussion on atomic reference counts that
acq_rel semantics should be sufficient for reference counts. It also
turned out that that is all that the definition of "Ordered" in the
documentation really guarantees.

Original-patch-by: Kevin Kofler <kevin.kofler@chello.at>
Change-Id: I6cd50b34bdc699ff8b7c8fe6d303b28d71494731
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Reviewed-by: Marc Mutz <marc.mutz@qt.io>
This commit is contained in:
Fabian Kosmale 2022-02-04 11:50:45 +01:00
parent 4d8cbf9863
commit e400ba64a3

View File

@ -278,13 +278,27 @@ template <typename X> struct QAtomicOps
template <typename T>
static inline bool ref(std::atomic<T> &_q_value)
{
return ++_q_value != 0;
/* Conceptually, we want to
* return ++_q_value != 0;
* However, that would be sequentially consistent, and thus stronger
* than what we need. Based on
* http://eel.is/c++draft/atomics.types.memop#6, we know that
* pre-increment is equivalent to fetch_add(1) + 1. Unlike
* pre-increment, fetch_add takes a memory order argument, so we can get
* the desired acquire-release semantics.
* One last gotcha is that fetch_add(1) + 1 would need to be converted
* back to T, because it's susceptible to integer promotion. To sidestep
* this issue and to avoid UB on signed overflow, we rewrite the
* expression to:
*/
return _q_value.fetch_add(1, std::memory_order_acq_rel) != T(-1);
}
template <typename T>
static inline bool deref(std::atomic<T> &_q_value) noexcept
{
return --_q_value != 0;
// compare with ref
return _q_value.fetch_sub(1, std::memory_order_acq_rel) != T(1);
}
static inline bool isTestAndSetNative() noexcept