QAtomicInteger Class
The QAtomicInteger class provides platform-independent atomic operations on integers. More...
Header: | #include <QAtomicInteger> |
qmake: | QT += core |
Since: | Qt 5.3 |
Inherits: | QBasicAtomicInteger |
Inherited By: |
This class was introduced in Qt 5.3.
Macros
Detailed Description
For atomic operations on pointers, see the QAtomicPointer class.
An atomic operation is a complex operation that completes without interruption. The QAtomicInteger class provides atomic reference counting, test-and-set, fetch-and-store, and fetch-and-add for integers.
The template parameter T
must be a C++ integer type:
- 8-bit: char, signed char, unsigned char, qint8, quint8
- 16-bit: short, unsigned short, qint16, quint16, char16_t (C++11)
- 32-bit: int, unsigned int, qint32, quint32, char32_t (C++11)
- 64-bit: long long, unsigned long long, qint64, quint64
- platform-specific size: long, unsigned long
- pointer size: qintptr, quintptr, qptrdiff
Of the list above, only the 32-bit- and pointer-sized instantiations are guaranteed to work on all platforms. Support for other sizes depends on the compiler and processor architecture the code is being compiled for. To test whether the other types are supported, check the macro Q_ATOMIC_INT\e{nn}_IS_SUPPORTED
, where \e{nn}
is the number of bits desired.
The Atomic API
Reference counting
The ref() and deref() functions provide an efficient reference counting API. The return value of these functions are used to indicate when the last reference has been released. These functions allow you to implement your own implicitly shared classes.
MySharedType &MySharedType::operator=(const MySharedType &other) { (void) other.data->atomicInt.ref(); if (!data->atomicInt.deref()) { // The last reference has been released delete d; } d = other.d; return *this; }
Memory ordering
QAtomicInteger provides several implementations of the atomic test-and-set, fetch-and-store, and fetch-and-add functions. Each implementation defines a memory ordering semantic that describes how memory accesses surrounding the atomic instruction are executed by the processor. Since many modern architectures allow out-of-order execution and memory ordering, using the correct semantic is necessary to ensure that your application functions properly on all processors.
- Relaxed - memory ordering is unspecified, leaving the compiler and processor to freely reorder memory accesses.
- Acquire - memory access following the atomic operation (in program order) may not be re-ordered before the atomic operation.
- Release - memory access before the atomic operation (in program order) may not be re-ordered after the atomic operation.
- Ordered - the same Acquire and Release semantics combined.
Test-and-set
If the current value of the QAtomicInteger is an expected value, the test-and-set functions assign a new value to the QAtomicInteger and return true. If values are not the same, these functions do nothing and return false. This operation equates to the following code:
if (currentValue == expectedValue) { currentValue = newValue; return true; } return false;
There are 4 test-and-set functions: testAndSetRelaxed(), testAndSetAcquire(), testAndSetRelease(), and testAndSetOrdered(). See above for an explanation of the different memory ordering semantics.
Fetch-and-store
The atomic fetch-and-store functions read the current value of the QAtomicInteger and then assign a new value, returning the original value. This operation equates to the following code:
int originalValue = currentValue; currentValue = newValue; return originalValue;
There are 4 fetch-and-store functions: fetchAndStoreRelaxed(), fetchAndStoreAcquire(), fetchAndStoreRelease(), and fetchAndStoreOrdered(). See above for an explanation of the different memory ordering semantics.
Fetch-and-add
The atomic fetch-and-add functions read the current value of the QAtomicInteger and then add the given value to the current value, returning the original value. This operation equates to the following code:
int originalValue = currentValue; currentValue += valueToAdd; return originalValue;
There are 4 fetch-and-add functions: fetchAndAddRelaxed(), fetchAndAddAcquire(), fetchAndAddRelease(), and fetchAndAddOrdered(). See above for an explanation of the different memory ordering semantics.
Feature Tests for the Atomic API
Providing a platform-independent atomic API that works on all processors is challenging. The API provided by QAtomicInteger is guaranteed to work atomically on all processors. However, since not all processors implement support for every operation provided by QAtomicInteger, it is necessary to expose information about the processor.
You can check at compile time which features are supported on your hardware using various macros. These will tell you if your hardware always, sometimes, or does not support a particular operation. The macros have the form Q_ATOMIC_INTnn_OPERATION_IS_HOW_NATIVE. nn is the size of the integer (in bits), OPERATION is one of REFERENCE_COUNTING, TEST_AND_SET, FETCH_AND_STORE, or FETCH_AND_ADD, and HOW is one of ALWAYS, SOMETIMES, or NOT. There will always be exactly one defined macro per operation. For example, if Q_ATOMIC_INT32_REFERENCE_COUNTING_IS_ALWAYS_NATIVE is defined, neither Q_ATOMIC_INT_REFERENCE_COUNTING_IS_SOMETIMES_NATIVE nor Q_ATOMIC_INT32_REFERENCE_COUNTING_IS_NOT_NATIVE will be defined.
An operation that completes in constant time is said to be wait-free. Such operations are not implemented using locks or loops of any kind. For atomic operations that are always supported, and that are wait-free, Qt defines the Q_ATOMIC_INTnn_OPERATION_IS_WAIT_FREE in addition to the Q_ATOMIC_INTnn_OPERATION_IS_ALWAYS_NATIVE.
In cases where an atomic operation is only supported in newer generations of the processor, QAtomicInteger also provides a way to check at runtime what your hardware supports with the isReferenceCountingNative(), isTestAndSetNative(), isFetchAndStoreNative(), and isFetchAndAddNative() functions. Wait-free implementations can be detected using the isReferenceCountingWaitFree(), isTestAndSetWaitFree(), isFetchAndStoreWaitFree(), and isFetchAndAddWaitFree() functions.
Below is a complete list of all feature macros for QAtomicInteger:
- Q_ATOMIC_INTnn_REFERENCE_COUNTING_IS_ALWAYS_NATIVE
- Q_ATOMIC_INTnn_REFERENCE_COUNTING_IS_SOMETIMES_NATIVE
- Q_ATOMIC_INTnn_REFERENCE_COUNTING_IS_NOT_NATIVE
- Q_ATOMIC_INTnn_REFERENCE_COUNTING_IS_WAIT_FREE
- Q_ATOMIC_INTnn_TEST_AND_SET_IS_ALWAYS_NATIVE
- Q_ATOMIC_INTnn_TEST_AND_SET_IS_SOMETIMES_NATIVE
- Q_ATOMIC_INTnn_TEST_AND_SET_IS_NOT_NATIVE
- Q_ATOMIC_INTnn_TEST_AND_SET_IS_WAIT_FREE
- Q_ATOMIC_INTnn_FETCH_AND_STORE_IS_ALWAYS_NATIVE
- Q_ATOMIC_INTnn_FETCH_AND_STORE_IS_SOMETIMES_NATIVE
- Q_ATOMIC_INTnn_FETCH_AND_STORE_IS_NOT_NATIVE
- Q_ATOMIC_INTnn_FETCH_AND_STORE_IS_WAIT_FREE
- Q_ATOMIC_INTnn_FETCH_AND_ADD_IS_ALWAYS_NATIVE
- Q_ATOMIC_INTnn_FETCH_AND_ADD_IS_SOMETIMES_NATIVE
- Q_ATOMIC_INTnn_FETCH_AND_ADD_IS_NOT_NATIVE
- Q_ATOMIC_INTnn_FETCH_AND_ADD_IS_WAIT_FREE
For compatibility with previous versions of Qt, macros with an empty nn are equivalent to the 32-bit macros. For example, Q_ATOMIC_INT_REFERENCE_COUNTING_IS_WAIT_FREE is the same as Q_ATOMIC_INT32_REFERENCE_COUNTING_IS_WAIT_FREE.
See also QAtomicPointer.
Macro Documentation
QAtomicInteger::Q_ATOMIC_INTnn_FETCH_AND_ADD_IS_ALWAYS_NATIVE
This macro is defined if and only if your processor supports atomic fetch-and-add on integers.
nn is the size of the integer, in bits (8, 16, 32 or 64).
QAtomicInteger::Q_ATOMIC_INTnn_FETCH_AND_ADD_IS_NOT_NATIVE
This macro is defined when the hardware does not support atomic fetch-and-add on integers.
nn is the size of the integer, in bits (8, 16, 32 or 64).
QAtomicInteger::Q_ATOMIC_INTnn_FETCH_AND_ADD_IS_SOMETIMES_NATIVE
This macro is defined when only certain generations of the processor support atomic fetch-and-add on integers. Use the QAtomicInteger<T>::isFetchAndAddNative() function to check what your processor supports.
nn is the size of the integer, in bits (8, 16, 32 or 64).
QAtomicInteger::Q_ATOMIC_INTnn_FETCH_AND_ADD_IS_WAIT_FREE
This macro is defined together with Q_ATOMIC_INTnn_FETCH_AND_ADD_IS_ALWAYS_NATIVE to indicate that the atomic fetch-and-add on integers is wait-free.
nn is the size of the integer, in bits (8, 16, 32 or 64).
QAtomicInteger::Q_ATOMIC_INTnn_FETCH_AND_STORE_IS_ALWAYS_NATIVE
This macro is defined if and only if your processor supports atomic fetch-and-store on integers.
nn is the size of the integer, in bits (8, 16, 32 or 64).
QAtomicInteger::Q_ATOMIC_INTnn_FETCH_AND_STORE_IS_NOT_NATIVE
This macro is defined when the hardware does not support atomic fetch-and-store on integers.
nn is the size of the integer, in bits (8, 16, 32 or 64).
QAtomicInteger::Q_ATOMIC_INTnn_FETCH_AND_STORE_IS_SOMETIMES_NATIVE
This macro is defined when only certain generations of the processor support atomic fetch-and-store on integers. Use the QAtomicInteger<T>::isFetchAndStoreNative() function to check what your processor supports.
nn is the size of the integer, in bits (8, 16, 32 or 64).
QAtomicInteger::Q_ATOMIC_INTnn_FETCH_AND_STORE_IS_WAIT_FREE
This macro is defined together with Q_ATOMIC_INTnn_FETCH_AND_STORE_IS_ALWAYS_NATIVE to indicate that the atomic fetch-and-store on integers is wait-free.
nn is the size of the integer, in bits (8, 16, 32 or 64).
QAtomicInteger::Q_ATOMIC_INTnn_IS_SUPPORTED
This macro is defined if atomic integers of size nn (in bits) are supported in this compiler / architecture combination. Q_ATOMIC_INT32_IS_SUPPORTED is always defined.
nn is the size of the integer, in bits (8, 16, 32 or 64).
QAtomicInteger::Q_ATOMIC_INTnn_REFERENCE_COUNTING_IS_ALWAYS_NATIVE
This macro is defined if and only if all generations of your processor support atomic reference counting.
nn is the size of the integer, in bits (8, 16, 32 or 64).
QAtomicInteger::Q_ATOMIC_INTnn_REFERENCE_COUNTING_IS_NOT_NATIVE
This macro is defined when the hardware does not support atomic reference counting.
nn is the size of the integer, in bits (8, 16, 32 or 64).
QAtomicInteger::Q_ATOMIC_INTnn_REFERENCE_COUNTING_IS_SOMETIMES_NATIVE
This macro is defined when only certain generations of the processor support atomic reference counting. Use the QAtomicInteger<T>::isReferenceCountingNative() function to check what your processor supports.
nn is the size of the integer, in bits (8, 16, 32 or 64).
QAtomicInteger::Q_ATOMIC_INTnn_REFERENCE_COUNTING_IS_WAIT_FREE
This macro is defined together with Q_ATOMIC_INTnn_REFERENCE_COUNTING_IS_ALWAYS_NATIVE to indicate that the reference counting is wait-free.
nn is the size of the integer, in bits (8, 16, 32 or 64).
QAtomicInteger::Q_ATOMIC_INTnn_TEST_AND_SET_IS_ALWAYS_NATIVE
This macro is defined if and only if your processor supports atomic test-and-set on integers.
nn is the size of the integer, in bits (8, 16, 32 or 64).
QAtomicInteger::Q_ATOMIC_INTnn_TEST_AND_SET_IS_NOT_NATIVE
This macro is defined when the hardware does not support atomic test-and-set on integers.
nn is the size of the integer, in bits (8, 16, 32 or 64).
QAtomicInteger::Q_ATOMIC_INTnn_TEST_AND_SET_IS_SOMETIMES_NATIVE
This macro is defined when only certain generations of the processor support atomic test-and-set on integers. Use the QAtomicInteger<T>::isTestAndSetNative() function to check what your processor supports.
nn is the size of the integer, in bits (8, 16, 32 or 64).
QAtomicInteger::Q_ATOMIC_INTnn_TEST_AND_SET_IS_WAIT_FREE
This macro is defined together with Q_ATOMIC_INTnn_TEST_AND_SET_IS_ALWAYS_NATIVE to indicate that the atomic test-and-set on integers is wait-free.
nn is the size of the integer, in bits (8, 16, 32 or 64).