1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
|
Before using the facilities introduced in this section the tthi(atomic) header
file must be included.
When data are shared among multiple threads, data corruption is usually
prevented using mutexes. To increment a simple tt(int) using this strategy
code as shown below is commonly used:
verb( {
lock_guard<mutex> lk{ intVarMutex };
++intVar;
})
The compound statement is used to limit the tt(lock_guard's) lifetime, so
that tt(intVar) is only locked for a short little while.
This scheme is not complex, but at the end of the day having to define a
tt(lock_guard) for every single use of a simple variable, and having to define
a matching mutex for each simple variable is a bit annoying and cumbersome.
bf(C++) offers a way out through the use of emi(atomic data types).
Atomic data types are available for all basic types, and also for (trivial)
user defined types. Trivial types are (see also section ref(TYPETRAITS)) all
scalar types, arrays of elements of a trivial type, and classes whose
constructors, copy constructors, and destructors all have default
implementations, and their non-static data members are themselves of trivial
types.
The class template hi(atomic<Type>)tt(std::atomic<Type>) is available for all
built-in types, including pointer types. E.g., tt(std::atomic<bool>) defines
an atomic tt(bool) type. For many types alternative somewhat shorter
type names are available. E.g, instead of tt(std::atomic<unsigned short>) the
type tt(std::atomic_ushort) can be used. Refer to the tt(atomic) header file
for a complete list of alternate names.
If tt(Trivial) is a user-defined trivial type then
hi(atomic<Trivial>)tt(std::atomic<Trivial>)
defines an atomic variant of tt(Trivial): such a type does not require
a separate tt(mutex) to synchronize access by multiple threads.
Objects of the class template tt(std::atomic<Type>) cannot directly be copied
or assigned to each other. However, they can be initialized by values of type
tt(Type), and values of type tt(Type) can also directly be assigned to
tt(std::atomic<Type>) objects. Moreover, since tt(atomic<Type>) types offer
conversion operators returning their tt(Type) values, an tt(atomic<Type>)
objects can also be assigned to or initialized by another tt(atomic<Type>)
object using a tt(static_cast):
verb( atomic<int> a1 = 5;
atomic<int> a2{ static_cast<int>(a1) };)
The class tt(std::atomic<Type>) provides several public members, shown
below. Non-member (free) functions operating on tt(atomic<Type>) objects are
also available.
The tt(std::memory_order) enumeration defines the following symbolic
constants, which are used to specify ordering constraints of atomic operations:
itemization(
itt(memory_order_acq_rel:) the operation must be a read-modify-write
operation, combining tt(memory_order_acquire) and
tt(memory_order_release);
itt(memory_order_acquire:) the operation is an acquire operation. It
synchronizes with a release operation that wrote the same memory
location;
itt(memory_order_consume:) the operation is a consume operation on the
involved memory location;
itt(memory_order_relaxed:) no ordering constraints are provided by the
operation;
itt(memory_order_release:) the operation is a release operation. It
synchronizes with acquire operations on the same location;
itt(memory_order_sec_cst:) the default memory order specification for all
operations. Memory storing operations use tt(memory_order_release),
memory load operations use tt(memory_order_acquire), and
read-modify-write operations use tt(memory_order_acq_rel).
)
The memory order cannot be specified for the overloaded operators provided by
tt(atomic<Type>). Otherwise, most tt(atomic) member functions may also be
given a final tt(memory_order) argument. Where this is not available it is
explictly mentioned at the function's description.
Here are the standard available tt(std::atomic<Type>) member functions:
itemization(
ithtq(compare_exchange_strong)(bool compare_exchange_strong(Type
¤tValue, Type newValue) noexcept)
(The value in the atomic object is compared to tt(newValue) using
byte-wise comparisons. If equal (and tt(true) is returned) then
tt(newValue) is stored in the atomic object; if unequal (and tt(false)
is returned) the object's current value is stored in
tt(currentValue);)
ithtq(compare_exchange_weak)(bool compare_exchange_weak(Type &oldValue,
Type newValue) noexcept)
(The value in the atomic object is compared to tt(newValue) using
byte-wise comparisons. If equal (and tt(true) is returned),
then tt(newValue) is stored in the atomic object; if unequal, or
tt(newValue) cannot be atomically assigned to the current object
tt(false) is returned and the object's current value is stored in
tt(currentValue);)
ithtq(exchange)(Type exchange(Type newValue) noexcept)
(The object's current value is returned, and tt(newValue) is assigned
to the current object;)
ithtq(is_lock_free)(bool is_lock_free() const noexept)
(If the operations on the current object can be performed lock-free
tt(true) is returned, otherwise tt(false).
This member has no tt(memory_order) parameter;)
ithtq(load)(Type load() const noexcept)
(The object's value is returned;)
ittq(operator Type() const noexcept)
(The object's value is returned;)
ithtq(store)(void store(Type newValue) noexcept)
(tt(NewValue) is assigned to the current object. Note that the standard
assignment operator can also be used.)
)
In addition to the above members, integral atomic types `tt(Integral)'
(essentially the atomic variants of all built-in integral types) also offer
the following member functions:
itemization(
ithtq(fetch_add)(Integral fetch_add(Integral value) noexcept)
(tt(Value) is added to the object's value, and the object's
value at the time of the call is returned;)
ithtq(fetch_sub)(Integral fetch_sub(Integral value) noexcept)
(tt(Value) is subtracted from the object's value, and the object's
value at the time of the call is returned;)
ithtq(fetch_and)(Integral fetch_and(Integral mask) noexcept)
(The tt(bit-and) operator is applied to the object's value and
tt(mask), assigning the resulting value to the current object. The
object's value at the time of the call is returned;)
ithtq(fetch_|=)(Integral fetch_|=(Integral mask) noexcept)
(The tt(bit-or) operator is applied to the object's value and tt(mask),
assigning the resulting value to the current object. The object's
value at the time of the call is returned;)
ithtq(fetch_^=)(Integral fetch_^=(Integral mask) noexcept)
(The tt(bit-xor) operator is applied to the object's value and
tt(mask), assigning the resulting value to the current object. The
object's value at the time of the call is returned;)
ithtq(operator++)(Integral operator++() noexcept)
(The prefix increment operator, returning object's new value;)
ithtq(operator++)(Integral operator++(int) noexcept)
(The postfix increment operator, returning the object's value before it
was incremented;)
it()hi(operator--)ttNoCt(Integral operator--() noexcept)\
quote(The prefix decrement operator, returning object's new value;)
it()hi(operator--)ttNoCt(Integral operator--(int) noexcept)\
quote(The postfix decrement operator, returning the object's value
before it was decremented;)
ithtq(operator+=)(Integral operator+=(Integral value) noexcept)
(tt(Value) is added to the object's current value and the object's
new value is returned;)
ithtq(operator-=)(Integral operator-=(Integral value) noexcept)
(tt(Value) is subtracted from the object's current value and the
object's new value is returned;)
ithtq(operator&=)(Integral operator&=(Integral mask) noexcept)
(The tt(bit-and) operator is applied to the object's current value and
tt(mask), assigning the resulting value to the current object. The
object's new value is returned;)
ithtq(operator|=)(Integral operator|=(Integral mask) noexcept)
(The tt(bit-or) operator is applied to the object's current value and
tt(mask), assigning the resulting value to the current object. The
object's new value is returned;)
ithtq(operator^=)(Integral operator^=(Integral mask) noexcept)
(The tt(bit-xor) operator is applied to the object's current value and
tt(mask), assigning the resulting value to the current object. The
object's new value is returned;)
)
Some of the free member functions have names ending in tt(_explicit). The
tt(_explicit) functions define an additional parameter `ti(memory_order)
tt(order)', which is not available for the non-tt(_explicit) functions (e.g.,
tt(atomic_load(atomic<Type> *ptr)) and tt(atomic_load_explicit(atomic<Type>
*ptr, memory_order order)))
Here are the free functions that are available for all atomic types:
itemization(
ithtq(atomic_compare_exchange_strong+nop()(_explicit))(bool
std::atomic_compare_exchange_strong(_explicit)(std::atomic<Type> *ptr,
Type *oldValue, Type newValue) noexept)
(returns tt(ptr->compare_exchange_strong(*oldValue, newValue));)
ithtq(atomic_compare_exchange_weak(_explicit))(bool
std::atomic_compare_exchange_weak(_explicit)(std::atomic<Type> *ptr,
Type *oldValue, Type newValue) noexept)
(returns tt(ptr->compare_exchange_weak(*oldValue, newValue));)
ithtq(atomic_exchange(_explicit))(Type
std::atomic_exchange(_explicit)(std::atomic<Type> *ptr, Type newValue)
noexept)
(returns tt(ptr->exchange(newValue));)
ithtq(atomic_init)(void std::atomic_init(std::atomic<Type> *ptr, Type
init) noexept)
(Stores tt(init) em(non)-atomically in tt(*ptr). The object pointed to
by tt(ptr) must have been default constructed, and as yet no member
functions must have been called for it.
This function has no tt(memory_order) parameter;)
ithtq(atomic_is_lock_free)(bool std::atomic_is_lock_free(std::atomic<Type>
const *ptr) noexept)
(returns tt(ptr->is_lock_free()).
This function has no tt(memory_order) parameter;)
ithtq(atomic_load(_explicit))(Type
std::atomic_load(_explicit)(std::atomic<Type> *ptr) noexept)
(returns tt(ptr->load());)
ithtq(atomic_store(_explicit))(void
std::atomic_store(_explicit)(std::atomic<Type> *ptr, Type value)
noexept)
(calls tt(ptr->store(value)).)
)
In addition to the abovementioned free functions tt(atomic<Integral>) types
also offer the following free member functions:
itemization(
ithtq(atomic_fetch_add(_explicit))(Integral
std::atomic_fetch_add(_explicit)(std::atomic<Integral> *ptr, Integral
value) noexcept)
(returns tt(ptr->fetch_add(value));)
ithtq(atomic_fetch_sub(_explicit))(Integral
std::atomic_fetch_sub(_explicit)(std::atomic<Integral> *ptr, Integral
value) noexcept)
(returns tt(ptr->fetch_sub(value));)
ithtq(atomic_fetch_and)(Integral
std::atomic_fetch_and(_explicit)(std::atomic<Integral> *ptr, Integral
mask) noexcept)
(returns tt(ptr->fetch_and(value));)
ithtq(atomic_fetch_or)(Integral
std::atomic_fetch_or(_explicit)(std::atomic<Integral> *ptr, Integral
mask) noexcept)
(returns tt(ptr->fetch_or(value));)
ithtq(atomic_fetch_xor)(Integral
std::atomic_fetch_xor(_explicit)(std::atomic<Integral> *ptr, Integral
mask) noexcept)
(returns tt(ptr->fetch_xor(mask)).)
)
|