473 lines
16 KiB
Markdown
473 lines
16 KiB
Markdown
[intro.races]
|
||
|
||
# 6 Basics [[basic]](./#basic)
|
||
|
||
## 6.10 Program execution [[basic.exec]](basic.exec#intro.races)
|
||
|
||
### 6.10.2 Multi-threaded executions and data races [[intro.multithread]](intro.multithread#intro.races)
|
||
|
||
#### 6.10.2.2 Data races [intro.races]
|
||
|
||
[1](#1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/basic.tex#L6607)
|
||
|
||
The value of an object visible to a thread T at a particular point is the
|
||
initial value of the object, a value assigned to the object by T, or a
|
||
value assigned to the object by another thread, according to the rules below[.](#1.sentence-1)
|
||
|
||
[*Note [1](#note-1)*:
|
||
|
||
In some cases, there might instead be undefined behavior[.](#1.sentence-2)
|
||
|
||
Much of this
|
||
subclause is motivated by the desire to support atomic operations with explicit
|
||
and detailed visibility constraints[.](#1.sentence-3)
|
||
|
||
However, it also implicitly supports a
|
||
simpler view for more restricted programs[.](#1.sentence-4)
|
||
|
||
â *end note*]
|
||
|
||
[2](#2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/basic.tex#L6618)
|
||
|
||
Two expression evaluations [*conflict*](#def:conflict "6.10.2.2 Data races [intro.races]") if one of them
|
||
|
||
- [(2.1)](#2.1)
|
||
|
||
modifies ([[defns.access]](defns.access "3.1 access")) a memory location ([[intro.memory]](intro.memory "6.8.1 Memory model")) or
|
||
|
||
- [(2.2)](#2.2)
|
||
|
||
starts or ends the lifetime of an object in a memory location
|
||
|
||
and the other one
|
||
|
||
- [(2.3)](#2.3)
|
||
|
||
reads or modifies the same memory location or
|
||
|
||
- [(2.4)](#2.4)
|
||
|
||
starts or ends the lifetime of an object occupying storage that
|
||
overlaps with the memory location[.](#2.sentence-1)
|
||
|
||
[*Note [2](#note-2)*:
|
||
|
||
A modification can still conflict
|
||
even if it does not alter the value of any bits[.](#2.sentence-2)
|
||
|
||
â *end note*]
|
||
|
||
[3](#3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/basic.tex#L6639)
|
||
|
||
The library defines a number of atomic operations ([[atomics]](atomics "32.5 Atomic operations")) and
|
||
operations on mutexes ([[thread]](thread "32 Concurrency support library")) that are specially identified as
|
||
synchronization operations[.](#3.sentence-1)
|
||
|
||
These operations play a special role in making
|
||
assignments in one thread visible to another[.](#3.sentence-2)
|
||
|
||
A synchronization operation on one
|
||
or more memory locations is either an acquire operation, a
|
||
release operation, or both an acquire and release operation[.](#3.sentence-3)
|
||
|
||
A synchronization
|
||
operation without an associated memory location is a fence and can be either an
|
||
acquire fence, a release fence, or both an acquire and release fence[.](#3.sentence-4)
|
||
|
||
In
|
||
addition, there are relaxed atomic operations, which are not synchronization
|
||
operations, and atomic read-modify-write operations, which have special
|
||
characteristics[.](#3.sentence-5)
|
||
|
||
[*Note [3](#note-3)*:
|
||
|
||
For example, a call that acquires a mutex will
|
||
perform an acquire operation on the locations comprising the mutex[.](#3.sentence-6)
|
||
|
||
Correspondingly, a call that releases the same mutex will perform a release
|
||
operation on those same locations[.](#3.sentence-7)
|
||
|
||
Informally, performing a release operation onA forces priorside effects on other memory locations to become visible
|
||
to other threads that later perform a consume or an acquire operation onA[.](#3.sentence-8)
|
||
|
||
âRelaxedâ atomic operations are not synchronization operations even
|
||
though, like synchronization operations, they cannot contribute to data races[.](#3.sentence-9)
|
||
|
||
â *end note*]
|
||
|
||
[4](#4)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/basic.tex#L6664)
|
||
|
||
All modifications to a particular atomic object M occur in some
|
||
particular total order, called the [*modification order*](#def:modification_order "6.10.2.2 Data races [intro.races]") of M[.](#4.sentence-1)
|
||
|
||
[*Note [4](#note-4)*:
|
||
|
||
There is a separate order for each
|
||
atomic object[.](#4.sentence-2)
|
||
|
||
There is no requirement that these can be combined into a single
|
||
total order for all objects[.](#4.sentence-3)
|
||
|
||
In general this will be impossible since different
|
||
threads can observe modifications to different objects in inconsistent orders[.](#4.sentence-4)
|
||
|
||
â *end note*]
|
||
|
||
[5](#5)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/basic.tex#L6674)
|
||
|
||
A [*release sequence*](#def:release_sequence "6.10.2.2 Data races [intro.races]") headed
|
||
by a release operation A on an atomic object M is a maximal contiguous sub-sequence ofside effects in the modification order of M,
|
||
where the first operation is A, and
|
||
every subsequent operation is an atomic read-modify-write operation[.](#5.sentence-1)
|
||
|
||
[6](#6)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/basic.tex#L6683)
|
||
|
||
Certain library calls [*synchronize with*](#def:synchronize_with "6.10.2.2 Data races [intro.races]") other library calls performed by
|
||
another thread[.](#6.sentence-1)
|
||
|
||
For example, an atomic store-release synchronizes with a
|
||
load-acquire that takes its value from the store ([[atomics.order]](atomics.order "32.5.4 Order and consistency"))[.](#6.sentence-2)
|
||
|
||
[*Note [5](#note-5)*:
|
||
|
||
Except in the specified cases, reading a later value does not
|
||
necessarily ensure visibility as described below[.](#6.sentence-3)
|
||
|
||
Such a requirement would
|
||
sometimes interfere with efficient implementation[.](#6.sentence-4)
|
||
|
||
â *end note*]
|
||
|
||
[*Note [6](#note-6)*:
|
||
|
||
The
|
||
specifications of the synchronization operations define when one reads the value
|
||
written by another[.](#6.sentence-5)
|
||
|
||
For atomic objects, the definition is clear[.](#6.sentence-6)
|
||
|
||
All operations
|
||
on a given mutex occur in a single total order[.](#6.sentence-7)
|
||
|
||
Each mutex acquisition âreads
|
||
the value writtenâ by the last mutex release[.](#6.sentence-8)
|
||
|
||
â *end note*]
|
||
|
||
[7](#7)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/basic.tex#L6700)
|
||
|
||
An evaluation A [*happens before*](#def:happens_before "6.10.2.2 Data races [intro.races]") an evaluation B (or, equivalently, B happens after A)
|
||
if either
|
||
|
||
- [(7.1)](#7.1)
|
||
|
||
A is sequenced before B, or
|
||
|
||
- [(7.2)](#7.2)
|
||
|
||
A synchronizes with B, or
|
||
|
||
- [(7.3)](#7.3)
|
||
|
||
A happens before X and X happens before B[.](#7.sentence-1)
|
||
|
||
[*Note [7](#note-7)*:
|
||
|
||
An evaluation does not happen before itself[.](#7.sentence-2)
|
||
|
||
â *end note*]
|
||
|
||
[8](#8)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/basic.tex#L6713)
|
||
|
||
An evaluation A [*strongly happens before*](#def:strongly_happens_before "6.10.2.2 Data races [intro.races]") an evaluation D if, either
|
||
|
||
- [(8.1)](#8.1)
|
||
|
||
A is sequenced before D, or
|
||
|
||
- [(8.2)](#8.2)
|
||
|
||
A synchronizes with D, and
|
||
both A and D are
|
||
sequentially consistent atomic operations ([[atomics.order]](atomics.order "32.5.4 Order and consistency")), or
|
||
|
||
- [(8.3)](#8.3)
|
||
|
||
there are evaluations B and C such that A is sequenced before B,B happens before C, andC is sequenced before D, or
|
||
|
||
- [(8.4)](#8.4)
|
||
|
||
there is an evaluation B such thatA strongly happens before B, andB strongly happens before D[.](#8.sentence-1)
|
||
|
||
[*Note [8](#note-8)*:
|
||
|
||
Informally, if A strongly happens before B,
|
||
then A appears to be evaluated before B in all contexts[.](#8.sentence-2)
|
||
|
||
â *end note*]
|
||
|
||
[9](#9)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/basic.tex#L6735)
|
||
|
||
A [*visible side effect*](#def:side_effects,visible "6.10.2.2 Data races [intro.races]") A on a scalar object or bit-field M with respect to a value computation B of M satisfies the
|
||
conditions:
|
||
|
||
- [(9.1)](#9.1)
|
||
|
||
A happens before B and
|
||
|
||
- [(9.2)](#9.2)
|
||
|
||
there is no otherside effect X to M such that A happens before X and X happens before B[.](#9.sentence-1)
|
||
|
||
The value of a non-atomic scalar object or bit-field M, as determined by
|
||
evaluation B, is the value stored by thevisible side effect A[.](#9.sentence-2)
|
||
|
||
[*Note [9](#note-9)*:
|
||
|
||
If there is ambiguity about which side effect to a
|
||
non-atomic object or bit-field is visible, then the behavior is either
|
||
unspecified or undefined[.](#9.sentence-3)
|
||
|
||
â *end note*]
|
||
|
||
[*Note [10](#note-10)*:
|
||
|
||
This states that operations on
|
||
ordinary objects are not visibly reordered[.](#9.sentence-4)
|
||
|
||
This is not actually detectable
|
||
without data races, but is needed to ensure that data races, as defined
|
||
below, and with suitable restrictions on the use of atomics, correspond to data
|
||
races in a simple interleaved (sequentially consistent) execution[.](#9.sentence-5)
|
||
|
||
â *end note*]
|
||
|
||
[10](#10)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/basic.tex#L6764)
|
||
|
||
The value of an
|
||
atomic object M, as determined by evaluation B, is the value
|
||
stored by some unspecified
|
||
side effect A that modifies M, where B does not happen
|
||
before A[.](#10.sentence-1)
|
||
|
||
[*Note [11](#note-11)*:
|
||
|
||
The set of such side effects is also restricted by the rest of the rules
|
||
described here, and in particular, by the coherence requirements below[.](#10.sentence-2)
|
||
|
||
â *end note*]
|
||
|
||
[11](#11)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/basic.tex#L6775)
|
||
|
||
If an operation A that modifies an atomic object M happens before
|
||
an operation B that modifies M, then A is earlier
|
||
than B in the modification order of M[.](#11.sentence-1)
|
||
|
||
[*Note [12](#note-12)*:
|
||
|
||
This requirement is known as write-write coherence[.](#11.sentence-2)
|
||
|
||
â *end note*]
|
||
|
||
[12](#12)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/basic.tex#L6784)
|
||
|
||
If avalue computation A of an atomic object M happens before a
|
||
value computation B of M, and A takes its value from a side
|
||
effect X on M, then the value computed by B is either
|
||
the value stored by X or the value stored by aside effect Y on M,
|
||
where Y follows X in the modification order of M[.](#12.sentence-1)
|
||
|
||
[*Note [13](#note-13)*:
|
||
|
||
This requirement is known as read-read coherence[.](#12.sentence-2)
|
||
|
||
â *end note*]
|
||
|
||
[13](#13)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/basic.tex#L6799)
|
||
|
||
If avalue computation A of an atomic object M happens before an
|
||
operation B that modifies M, then A takes its value from a side
|
||
effect X on M, where X precedes B in the
|
||
modification order of M[.](#13.sentence-1)
|
||
|
||
[*Note [14](#note-14)*:
|
||
|
||
This requirement is known as
|
||
read-write coherence[.](#13.sentence-2)
|
||
|
||
â *end note*]
|
||
|
||
[14](#14)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/basic.tex#L6812)
|
||
|
||
If aside effect X on an atomic object M happens before a value
|
||
computation B of M, then the evaluation B takes its
|
||
value from X or from aside effect Y that follows X in the modification order of M[.](#14.sentence-1)
|
||
|
||
[*Note [15](#note-15)*:
|
||
|
||
This requirement is known as write-read coherence[.](#14.sentence-2)
|
||
|
||
â *end note*]
|
||
|
||
[15](#15)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/basic.tex#L6825)
|
||
|
||
[*Note [16](#note-16)*:
|
||
|
||
The four preceding coherence requirements effectively disallow
|
||
compiler reordering of atomic operations to a single object, even if both
|
||
operations are relaxed loads[.](#15.sentence-1)
|
||
|
||
This effectively makes the cache coherence
|
||
guarantee provided by most hardware available to C++ atomic operations[.](#15.sentence-2)
|
||
|
||
â *end note*]
|
||
|
||
[16](#16)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/basic.tex#L6833)
|
||
|
||
[*Note [17](#note-17)*:
|
||
|
||
The value observed by a load of an atomic depends on the âhappens
|
||
beforeâ relation, which depends on the values observed by loads of atomics[.](#16.sentence-1)
|
||
|
||
The intended reading is that there must exist an
|
||
association of atomic loads with modifications they observe that, together with
|
||
suitably chosen modification orders and the âhappens beforeâ relation derived
|
||
as described above, satisfy the resulting constraints as imposed here[.](#16.sentence-2)
|
||
|
||
â *end note*]
|
||
|
||
[17](#17)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/basic.tex#L6843)
|
||
|
||
Two actions are [*potentially concurrent*](#def:potentially_concurrent "6.10.2.2 Data races [intro.races]") if
|
||
|
||
- [(17.1)](#17.1)
|
||
|
||
they are performed by different threads, or
|
||
|
||
- [(17.2)](#17.2)
|
||
|
||
they are unsequenced, at least one is performed by a signal handler, and
|
||
they are not both performed by the same signal handler invocation[.](#17.sentence-1)
|
||
|
||
The execution of a program contains a [*data race*](#def:data_race "6.10.2.2 Data races [intro.races]") if it contains two
|
||
potentially concurrent conflicting actions, at least one of which is not atomic,
|
||
and neither happens before the other,
|
||
except for the special case for signal handlers described below[.](#17.sentence-2)
|
||
|
||
Any such data race results in undefined
|
||
behavior[.](#17.sentence-3)
|
||
|
||
[*Note [18](#note-18)*:
|
||
|
||
It can be shown that programs that correctly use mutexes
|
||
and memory_order::seq_cst operations to prevent all data races and use no
|
||
other synchronization operations behave as if the operations executed by their
|
||
constituent threads were simply interleaved, with eachvalue computation of an
|
||
object being taken from the lastside effect on that object in that
|
||
interleaving[.](#17.sentence-4)
|
||
|
||
This is normally referred to as âsequential consistencyâ[.](#17.sentence-5)
|
||
|
||
However, this applies only to data-race-free programs, and data-race-free
|
||
programs cannot observe most program transformations that do not change
|
||
single-threaded program semantics[.](#17.sentence-6)
|
||
|
||
In fact, most single-threaded program
|
||
transformations remain possible, since any program that behaves
|
||
differently as a result has undefined behavior[.](#17.sentence-7)
|
||
|
||
â *end note*]
|
||
|
||
[18](#18)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/basic.tex#L6874)
|
||
|
||
Two accesses to the same non-bit-field object
|
||
of type volatile std::sig_atomic_t do not
|
||
result in a data race if both occur in the same thread, even if one or more
|
||
occurs in a signal handler[.](#18.sentence-1)
|
||
|
||
For each signal handler invocation, evaluations
|
||
performed by the thread invoking a signal handler can be divided into two
|
||
groups A and B, such that no evaluations inB happen before evaluations in A, and the
|
||
evaluations of such volatile std::sig_atomic_t objects take values as though
|
||
all evaluations in A happened before the execution of the signal
|
||
handler and the execution of the signal handler happened before all evaluations
|
||
in B[.](#18.sentence-2)
|
||
|
||
[19](#19)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/basic.tex#L6887)
|
||
|
||
[*Note [19](#note-19)*:
|
||
|
||
Compiler transformations that introduce assignments to a potentially
|
||
shared memory location that would not be modified by the abstract machine are
|
||
generally precluded by this document, since such an assignment might overwrite
|
||
another assignment by a different thread in cases in which an abstract machine
|
||
execution would not have encountered a data race[.](#19.sentence-1)
|
||
|
||
This includes implementations
|
||
of data member assignment that overwrite adjacent members in separate memory
|
||
locations[.](#19.sentence-2)
|
||
|
||
Reordering of atomic loads in cases in which the atomics in question
|
||
might alias is also generally precluded, since this could violate the coherence
|
||
rules[.](#19.sentence-3)
|
||
|
||
â *end note*]
|
||
|
||
[20](#20)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/basic.tex#L6900)
|
||
|
||
[*Note [20](#note-20)*:
|
||
|
||
It is possible that transformations that introduce a speculative read of a potentially
|
||
shared memory location do not preserve the semantics of the C++ program as
|
||
defined in this document, since they potentially introduce a data race[.](#20.sentence-1)
|
||
|
||
However,
|
||
they are typically valid in the context of an optimizing compiler that targets a
|
||
specific machine with well-defined semantics for data races[.](#20.sentence-2)
|
||
|
||
They would be
|
||
invalid for a hypothetical machine that is not tolerant of races or provides
|
||
hardware race detection[.](#20.sentence-3)
|
||
|
||
â *end note*]
|