Movatterモバイル変換


[0]ホーム

URL:


6 Basics[basic]

6.9 Program execution[basic.exec]

6.9.2 Multi-threaded executions and data races[intro.multithread]

6.9.2.1 General[intro.multithread.general]

Athread of execution (also known as athread) is a single flow ofcontrol within a program, including the initial invocation of a specifictop-level function, and recursively including every function invocationsubsequently executed by the thread.
[Note 1:
When one thread creates another,the initial call to the top-level function of the new thread is executed by thenew thread, not by the creating thread.
— end note]
Every thread in a program canpotentially access every object and function in a program.50
Under a hostedimplementation, a C++ program can have more than one thread runningconcurrently.
The execution of each thread proceeds as defined by the remainderof this document.
The execution of the entire program consists of an executionof all of its threads.
[Note 2:
Usually the execution can be viewed as aninterleaving of all its threads.
However, some kinds of atomic operations, forexample, allow executions inconsistent with a simple interleaving, as describedbelow.
— end note]
Under a freestanding implementation, it isimplementation-defined whether a program canhave more than one thread of execution.
For a signal handler that is not executed as a result of a call to thestd​::​raise function, it is unspecified which thread of executioncontains the signal handler invocation.
An objectwith automatic or thread storage duration ([basic.stc]) is associated withone specific thread, and can be accessed by a different thread only indirectlythrough a pointer or reference ([basic.compound]).
 

6.9.2.2 Data races[intro.races]

The value of an object visible to a threadT at a particular point is theinitial value of the object, a value assigned to the object byT, or avalue assigned to the object by another thread, according to the rules below.
[Note 1:
In some cases, there might instead be undefined behavior.
Much of thissubclause is motivated by the desire to support atomic operations with explicitand detailed visibility constraints.
However, it also implicitly supports asimpler view for more restricted programs.
— end note]
Two expression evaluationsconflict if one of them modifies a memorylocation ([intro.memory]) and the other one reads or modifies the samememory location.
The library defines a number of atomic operations ([atomics]) andoperations on mutexes ([thread]) that are specially identified assynchronization operations.
These operations play a special role in makingassignments in one thread visible to another.
A synchronization operation on oneor more memory locations is either a consume operation, an acquire operation, arelease operation, or both an acquire and release operation.
A synchronizationoperation without an associated memory location is a fence and can be either anacquire fence, a release fence, or both an acquire and release fence.
Inaddition, there are relaxed atomic operations, which are not synchronizationoperations, and atomic read-modify-write operations, which have specialcharacteristics.
[Note 2:
For example, a call that acquires a mutex willperform an acquire operation on the locations comprising the mutex.
Correspondingly, a call that releases the same mutex will perform a releaseoperation on those same locations.
Informally, performing a release operation onA forces priorside effects on other memory locations to become visibleto other threads that later perform a consume or an acquire operation onA.
“Relaxed” atomic operations are not synchronization operations eventhough, like synchronization operations, they cannot contribute to data races.
— end note]
All modifications to a particular atomic objectM occur in someparticular total order, called themodification order ofM.
[Note 3:
There is a separate order for eachatomic object.
There is no requirement that these can be combined into a singletotal order for all objects.
In general this will be impossible since differentthreads might observe modifications to different objects in inconsistent orders.
— end note]
Arelease sequence headedby a release operationA on an atomic objectMis a maximal contiguous sub-sequence ofside effects in the modification order ofM,where the first operation isA, andevery subsequent operation is an atomic read-modify-write operation.
Certain library callssynchronize with other library calls performed byanother thread.
For example, an atomic store-release synchronizes with aload-acquire that takes its value from the store ([atomics.order]).
[Note 4:
Except in the specified cases, reading a later value does notnecessarily ensure visibility as described below.
Such a requirement wouldsometimes interfere with efficient implementation.
— end note]
[Note 5:
Thespecifications of the synchronization operations define when one reads the valuewritten by another.
For atomic objects, the definition is clear.
All operationson a given mutex occur in a single total order.
Each mutex acquisition “readsthe value written” by the last mutex release.
— end note]
An evaluationAcarries a dependency to an evaluationB if
  • the value ofA is used as an operand ofB, unless: or
  • A writes a scalar object or bit-fieldM,B reads the valuewritten byA fromM, andA is sequenced beforeB, or
  • for some evaluationX,A carries a dependency toX, andX carries a dependency toB.
[Note 6:
“Carries a dependency to” is a subset of “is sequenced before”,and is similarly strictly intra-thread.
— end note]
An evaluationA isdependency-ordered before an evaluationB if
  • A performs a release operation on an atomic objectM, and, inanother thread,B performs a consume operation onM and readsthe value written byA, or
  • for some evaluationX,A is dependency-ordered beforeX andX carries a dependency toB.
[Note 7:
The relation “is dependency-ordered before” is analogous to“synchronizes with”, but uses release/consume in place of release/acquire.
— end note]
An evaluationAinter-thread happens before an evaluationBif
  • A synchronizes withB, or
  • A is dependency-ordered beforeB, or
  • for some evaluationX
    • A synchronizes withX andX is sequenced beforeB, or
    • A is sequenced beforeX andX inter-thread happens beforeB, or
    • A inter-thread happens beforeX andX inter-thread happens beforeB.
[Note 8:
The “inter-thread happens before” relation describes arbitraryconcatenations of “sequenced before”, “synchronizes with” and“dependency-ordered before” relationships, with two exceptions.
The firstexception is that a concatenation is not permitted to end with“dependency-ordered before” followed by “sequenced before”.
The reason forthis limitation is that a consume operation participating in a“dependency-ordered before” relationship provides ordering only with respectto operations to which this consume operation actually carries a dependency.
Thereason that this limitation applies only to the end of such a concatenation isthat any subsequent release operation will provide the required ordering for aprior consume operation.
The second exception is that a concatenation is notpermitted to consist entirely of “sequenced before”.
The reasons for thislimitation are (1) to permit “inter-thread happens before” to be transitivelyclosed and (2) the “happens before” relation, defined below, provides forrelationships consisting entirely of “sequenced before”.
— end note]
An evaluationAhappens before an evaluationB(or, equivalently,Bhappens afterA) if:
  • A is sequenced beforeB, or
  • A inter-thread happens beforeB.
The implementation shall ensure that no program execution demonstrates a cyclein the “happens before” relation.
[Note 9:
This cycle would otherwise bepossible only through the use of consume operations.
— end note]
An evaluationAsimply happens before an evaluationBif either
  • A is sequenced beforeB, or
  • A synchronizes withB, or
  • A simply happens beforeX andX simply happens beforeB.
[Note 10:
In the absence of consume operations,the happens before and simply happens before relations are identical.
— end note]
An evaluationAstrongly happens beforean evaluationD if, either
  • A is sequenced beforeD, or
  • A synchronizes withD, andbothA andD aresequentially consistent atomic operations ([atomics.order]), or
  • there are evaluationsB andCsuch thatA is sequenced beforeB,B simply happens beforeC, andC is sequenced beforeD, or
  • there is an evaluationB such thatA strongly happens beforeB, andB strongly happens beforeD.
[Note 11:
Informally, ifA strongly happens beforeB,thenA appears to be evaluated beforeBin all contexts.
Strongly happens before excludes consume operations.
— end note]
Avisible side effectA on a scalar object or bit-fieldMwith respect to a value computationB ofM satisfies theconditions:
  • A happens beforeB and
  • there is no otherside effectX toM such thatAhappens beforeX andX happens beforeB.
The value of a non-atomic scalar object or bit-fieldM, as determined byevaluationB, shall be the value stored by thevisible side effectA.
[Note 12:
If there is ambiguity about which side effect to anon-atomic object or bit-field is visible, then the behavior is eitherunspecified or undefined.
— end note]
[Note 13:
This states that operations onordinary objects are not visibly reordered.
This is not actually detectablewithout data races, but it is necessary to ensure that data races, as definedbelow, and with suitable restrictions on the use of atomics, correspond to dataraces in a simple interleaved (sequentially consistent) execution.
— end note]
The value of anatomic objectM, as determined by evaluationB, shall be the valuestored by someside effectA that modifiesM, whereB does not happenbeforeA.
[Note 14:
The set of such side effects is also restricted by the rest of the rulesdescribed here, and in particular, by the coherence requirements below.
— end note]
If an operationA that modifies an atomic objectM happens beforean operationB that modifiesM, thenA shall be earlierthanB in the modification order ofM.
[Note 15:
This requirement is known as write-write coherence.
— end note]
If avalue computationA of an atomic objectM happens before avalue computationB ofM, andA takes its value from a sideeffectX onM, then the value computed byB shall either bethe value stored byX or the value stored by aside effectY onM,whereY followsX in the modification order ofM.
[Note 16:
This requirement is known as read-read coherence.
— end note]
If avalue computationA of an atomic objectM happens before anoperationB that modifiesM, thenA shall take its value from a sideeffectX onM, whereX precedesB in themodification order ofM.
[Note 17:
This requirement is known asread-write coherence.
— end note]
If aside effectX on an atomic objectM happens before a valuecomputationB ofM, then the evaluationB shall take itsvalue fromX or from aside effectY that followsX in the modification order ofM.
[Note 18:
This requirement is known as write-read coherence.
— end note]
[Note 19:
The four preceding coherence requirements effectively disallowcompiler reordering of atomic operations to a single object, even if bothoperations are relaxed loads.
This effectively makes the cache coherenceguarantee provided by most hardware available to C++ atomic operations.
— end note]
[Note 20:
The value observed by a load of an atomic depends on the “happensbefore” relation, which depends on the values observed by loads of atomics.
The intended reading is that there must exist anassociation of atomic loads with modifications they observe that, together withsuitably chosen modification orders and the “happens before” relation derivedas described above, satisfy the resulting constraints as imposed here.
— end note]
Two actions arepotentially concurrent if
  • they are performed by different threads, or
  • they are unsequenced, at least one is performed by a signal handler, andthey are not both performed by the same signal handler invocation.
The execution of a program contains adata race if it contains twopotentially 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.
Any such data race results in undefinedbehavior.
[Note 21:
It can be shown that programs that correctly use mutexesandmemory_­order​::​seq_­cst operations to prevent all data races and use noother synchronization operations behave as if the operations executed by theirconstituent threads were simply interleaved, with eachvalue computation of anobject being taken from the lastside effect on that object in thatinterleaving.
This is normally referred to as “sequential consistency”.
However, this applies only to data-race-free programs, and data-race-freeprograms cannot observe most program transformations that do not changesingle-threaded program semantics.
In fact, most single-threaded programtransformations continue to be allowed, since any program that behavesdifferently as a result has undefined behavior.
— end note]
Two accesses to the same object of typevolatile std​::​sig_­atomic_­t do notresult in a data race if both occur in the same thread, even if one or moreoccurs in a signal handler.
For each signal handler invocation, evaluationsperformed by the thread invoking a signal handler can be divided into twogroupsA andB, such that no evaluations inB happen before evaluations inA, and theevaluations of suchvolatile std​::​sig_­atomic_­t objects take values as thoughall evaluations inA happened before the execution of the signalhandler and the execution of the signal handler happened before all evaluationsinB.
[Note 22:
Compiler transformations that introduce assignments to a potentiallyshared memory location that would not be modified by the abstract machine aregenerally precluded by this document, since such an assignment might overwriteanother assignment by a different thread in cases in which an abstract machineexecution would not have encountered a data race.
This includes implementationsof data member assignment that overwrite adjacent members in separate memorylocations.
Reordering of atomic loads in cases in which the atomics in questionmight alias is also generally precluded, since this could violate the coherencerules.
— end note]
[Note 23:
Transformations that introduce a speculative read of a potentiallyshared memory location might not preserve the semantics of the C++ program asdefined in this document, since they potentially introduce a data race.
However,they are typically valid in the context of an optimizing compiler that targets aspecific machine with well-defined semantics for data races.
They would beinvalid for a hypothetical machine that is not tolerant of races or provideshardware race detection.
— end note]

6.9.2.3 Forward progress[intro.progress]

The implementation may assume that any thread will eventually do one of thefollowing:
  • terminate,
  • make a call to a library I/O function,
  • perform an access through a volatile glvalue, or
  • perform a synchronization operation or an atomic operation.
[Note 1:
This is intended to allow compiler transformations such as removal ofempty loops, even when termination cannot be proven.
— end note]
Executions of atomic functionsthat are either defined to be lock-free ([atomics.flag])or indicated as lock-free ([atomics.lockfree])arelock-free executions.
  • If there is only one thread that is not blocked ([defns.block]) in a standard library function, a lock-free execution in that thread shall complete.
    [Note 2:
    Concurrently executing threads might prevent progress of a lock-free execution.
    For example, this situation can occur with load-locked store-conditional implementations.
    This property is sometimes termed obstruction-free.
    — end note]
  • When one or more lock-free executions run concurrently, at least one should complete.
    [Note 3:
    It is difficult for some implementations to provide absolute guarantees to this effect, since repeated and particularly inopportune interference from other threads could prevent forward progress, e.g., by repeatedly stealing a cache line for unrelated purposes between load-locked and store-conditional instructions.
    For implementations that follow this recommendation and ensure that such effects cannot indefinitely delay progress under expected operating conditions, such anomalies can therefore safely be ignored by programmers.
    Outside this document, this property is sometimes termed lock-free.
    — end note]
During the execution of a thread of execution, each of the following is termedanexecution step:
  • termination of the thread of execution,
  • performing an access through a volatile glvalue, or
  • completion of a call to a library I/O function, a synchronization operation, or an atomic operation.
An invocation of a standard library function that blocks ([defns.block])is considered to continuously execute execution steps while waiting for thecondition that it blocks on to be satisfied.
[Example 1:
A library I/O function that blocks until the I/O operation is complete canbe considered to continuously check whether the operation is complete.
Eachsuch check might consist of one or more execution steps, for example usingobservable behavior of the abstract machine.
— end example]
[Note 4:
Because of this and the preceding requirement regarding what threads of executionhave to perform eventually, it follows that no thread of execution can executeforever without an execution step occurring.
— end note]
A thread of executionmakes progresswhen an execution step occurs or alock-free execution does not complete because there are other concurrent threadsthat are not blocked in a standard library function (see above).
For a thread of execution providingconcurrent forward progress guarantees,the implementation ensures that the thread will eventually make progress for aslong as it has not terminated.
[Note 5:
This is required regardless of whether or not other threads of executions (if any)have been or are making progress.
To eventually fulfill this requirement means thatthis will happen in an unspecified but finite amount of time.
— end note]
It isimplementation-defined whether theimplementation-created thread of execution that executesmain ([basic.start.main]) and the threads of execution created bystd​::​thread ([thread.thread.class])orstd​::​jthread ([thread.jthread.class])provide concurrent forward progress guarantees.
General-purpose implementations should provide these guarantees.
For a thread of execution providingparallel forward progress guarantees,the implementation is not required to ensure that the thread will eventually makeprogress if it has not yet executed any execution step; once this thread hasexecuted a step, it provides concurrent forward progress guarantees.
[Note 6:
This does not specify a requirement for when to start this thread of execution,which will typically be specified by the entity that creates this thread ofexecution.
For example, a thread of execution that provides concurrent forwardprogress guarantees and executes tasks from a set of tasks in an arbitrary order,one after the other, satisfies the requirements of parallel forward progress forthese tasks.
— end note]
For a thread of execution providingweakly parallel forward progressguarantees, the implementation does not ensure that the thread will eventuallymake progress.
[Note 7:
Threads of execution providing weakly parallel forward progress guarantees cannotbe expected to make progress regardless of whether other threads make progress ornot; however, blocking with forward progress guarantee delegation, as defined below,can be used to ensure that such threads of execution make progress eventually.
— end note]
Concurrent forward progress guarantees are stronger than parallel forward progressguarantees, which in turn are stronger than weakly parallel forward progressguarantees.
[Note 8:
For example, some kinds of synchronization between threads of execution might onlymake progress if the respective threads of execution provide parallel forward progressguarantees, but will fail to make progress under weakly parallel guarantees.
— end note]
When a thread of executionP is specified toblock with forward progress guarantee delegationon the completion of a setS of threads of execution,then throughout the whole time ofP being blocked onS,the implementation shall ensure that the forward progress guaranteesprovided by at least one thread of execution inSis at least as strong asP's forward progress guarantees.
[Note 9:
It is unspecified which thread or threads of execution inS are chosenand for which number of execution steps.
The strengthening is not permanent andnot necessarily in place for the rest of the lifetime of the affected thread ofexecution.
As long asP is blocked, the implementation has to eventuallyselect and potentially strengthen a thread of execution inS.
— end note]
Once a thread of execution inS terminates, it is removed fromS.
OnceS is empty,P is unblocked.
[Note 10:
A thread of executionB thus can temporarily provide an effectivelystronger forward progress guarantee for a certain amount of time, due to asecond thread of executionA being blocked on it with forwardprogress guarantee delegation.
In turn, ifB then blocks withforward progress guarantee delegation onC, this could also temporarilyprovide a stronger forward progress guarantee toC.
— end note]
[Note 11:
If all threads of execution inS finish executing (e.g., they terminateand do not use blocking synchronization incorrectly), thenP's executionof the operation that blocks with forward progress guarantee delegation will notresult inP's progress guarantee being effectively weakened.
— end note]
[Note 12:
This does not remove any constraints regarding blocking synchronization forthreads of execution providing parallel or weakly parallel forward progressguarantees because the implementation is not required to strengthen a particularthread of execution whose too-weak progress guarantee is preventing overall progress.
— end note]
An implementation should ensure that the last value (in modification order)assigned by an atomic or synchronization operation will become visible to allother threads in a finite period of time.

[8]ページ先頭

©2009-2026 Movatter.jp