4 General principles [intro]

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

4.7.1 Data races [intro.races]

Значение объекта, видимого потокуT в определенной точке, - это начальное значение объекта, значение, присвоенноеTобъекту другим потоком, или значение, присвоенное объекту другим потоком, в соответствии с приведенными ниже правилами. [ Note: В некоторых случаях вместо этого может быть неопределенное поведение. Большая часть этого раздела мотивирована желанием поддерживать атомарные операции с явными и подробными ограничениями видимости. Однако он также неявно поддерживает более простой вид для более ограниченных программ. ]end note

Два вычисления выражения,conflict если одно из них изменяет a, memory location а другое читает или изменяет одну и ту же ячейку памяти.

Библиотека определяет рядatomic operations операцийmutexes , которые специально определены как операции синхронизации. Эти операции играют особую роль в том, чтобы сделать назначения в одном потоке видимыми для другого. Операция синхронизации в одной или нескольких ячейках памяти является либо операцией потребления, либо операцией получения, либо операцией освобождения, либо одновременно операцией получения и освобождения. Операция синхронизации без связанной ячейки памяти является ограничением и может быть либо ограничением получения, либо ограничением разблокировки, либо одновременно ограничением захвата и разблокировки. Кроме того, существуют упрощенные атомарные операции, которые не являются операциями синхронизации, и атомарные операции чтения-изменения-записи, которые имеют особые характеристики. [ Note: Например, вызов, который получает мьютекс, будет выполнять операцию получения в местах, составляющих мьютекс. Соответственно, вызов, освобождающий тот же мьютекс, выполнит операцию освобождения в тех же местах. Неформально выполнение операции освобождения A заставляет предыдущие побочные эффекты в других ячейках памяти становиться видимыми для других потоков, которые позже выполняют операцию потребления или получения A. «Расслабленные» атомарные операции не являются операциями синхронизации, хотя, как и операции синхронизации, они не могут участвовать в гонке данных. ]end note

Все модификации конкретного атомарного объектаM происходят в определенном общем порядке, называемомmodification order ofM. [ Note: Для каждого атомарного объекта существует отдельный заказ. Не требуется, чтобы их можно было объединить в единый общий заказ для всех объектов. В общем, это будет невозможно, поскольку разные потоки могут наблюдать модификации разных объектов в несовместимом порядке. ]end note

release sequence Во главе с операцией освобожденияA на атомном объекте M является максимальной смежным субом-последовательность побочных эффектов в порядке модификацииM, где первая операция являетсяA, и каждой последующей операция

  • выполняется тем же потоком, который выполнялA, или

  • это атомарная операция чтения-изменения-записи.

Определенная библиотека вызываетsynchronize with другие вызовы библиотеки, выполняемые другим потоком. Например, атомарное освобождение хранилища синхронизируется с получением загрузки, которое берет свое значение из store ([atomics.order]). [ Note: За исключением указанных случаев, чтение более позднего значения не обязательно обеспечивает видимость, как описано ниже. Такое требование иногда мешает эффективной реализации. ] [ Спецификации операций синхронизации определяют, когда один читает значение, записанное другим. Для атомарных объектов определение ясное. Все операции с данным мьютексом выполняются в едином общем порядке. Каждое получение мьютекса «считывает значение, записанное» последним выпуском мьютекса. ]end noteNote: end note

От оценкиAcarries a dependency к оценке,B если

  • значениеA используется как операндB, если:

    или

  • A записывает скалярный объект или битовое полеM,B читает значение, записанноеA изM, иA упорядочивается доB, или

  • для некоторой оценкиX,A несет в себе зависимость кX, и X несет в себе зависимость кB.

[ Note: «Несет зависимость от» - это подмножество «упорядочено до» и аналогично строго внутри потока. ]end note

ОценкаA -dependency-ordered before это оценка, B если

  • A выполняет операцию выпуска над атомарным объектомM, а в другом потокеB выполняет операцию потребленияM и считывает значение, записанное любым побочным эффектом в последовательности выпуска, озаглавленнойA, или

  • для некоторой оценкиX,A упорядочивается по зависимостям раньшеX и X несет в себе зависимость отB.

[ Note: Отношение «предварительно упорядочено зависимостями» аналогично «синхронизируется с», но использует освобождение / потребление вместо выпуска / получения. ]end note

ОценкаAinter-thread happens before оценка,B если

  • A синхронизируется сB, или

  • A упорядочивается по зависимости раньшеB, или

  • для некоторой оценкиX

    • A синхронизируется с предыдущимX иX упорядочиваетсяB, или

    • A упорядочивается до,X аX межпоточное происходит раньшеB, или

    • A inter-thread происходит раньше,X аX inter-thread - раньшеB.

[ Note: Отношение «между потоками происходит до» описывает произвольные конкатенации отношений «упорядочено до», «синхронизируется с» и «упорядочено по зависимости до» с двумя исключениями. Первое исключение состоит в том, что конкатенация не может заканчиваться словами «в порядке зависимости до», за которым следует «упорядочено до». Причина этого ограничения заключается в том, что операция потребления, участвующая в соотношении «зависимость упорядочена до», обеспечивает упорядочение только в отношении операций, от которых эта операция потребления фактически несет зависимость. Причина, по которой это ограничение применяется только к концу такой конкатенации, состоит в том, что любая последующая операция освобождения будет обеспечивать необходимое упорядочение для предыдущей операции потребления. Второе исключение состоит в том, что конкатенация не может состоять полностью из «предшествующей последовательности». Причины этого ограничения: (1) разрешить транзитивное закрытие «межпотока происходит до» и (2) отношение «происходит до», определенное ниже, предусматривает отношения, полностью состоящие из «упорядочено до». ]end note

ОценкаAhappens before оценкаB (или, что то же самое,Bhappens afterA), если:

  • A стоит раньшеB, или

  • A межпоток происходит раньшеB.

Реализация должна гарантировать, что выполнение программы не демонстрирует цикл в отношении «произошло до». [ Note: В противном случае этот цикл был бы возможен только при использовании операций потребления. ]end note

ОценкаAstrongly happens before оценка,B если

  • A стоит раньшеB, или

  • A синхронизируется сB, или

  • A сильно бывает доX иX сильно бывает доB.

[ Note: В отсутствие операций потребления, то происходит раньше и строго происходит до того, как отношения идентичны. Сильно бывает до того, как существенно исключает операции потребления. ]end note

visible side effectA На объект скалярного или битового поляM относительно вычисления значенияB изM удовлетворяет условиям:

  • A происходит раньшеB и

  • нет никакой другой побочный эффект ,X чтобыM таким образом, чтоA происходит доX иX происходит доB.

Значение неатомарного скалярного объекта или битового поляM, определяемое оценкойB, должно быть значением, сохраненным видимым побочным эффектом A. [ Note: Если есть неопределенность в отношении того, какой побочный эффект для неатомарного объекта или битового поля является видимым, то поведение либо не определено, либо не определено. ] [ Это означает, что операции с обычными объектами не меняются явно. На самом деле это невозможно обнаружить без гонок данных, но необходимо гарантировать, что гонки данных, как определено ниже, и с соответствующими ограничениями на использование атомики, соответствуют гонкам данных в простом чередующемся (последовательно согласованном) выполнении. ]end noteNote: end note

Значение атомарного объектаM, определяемое оценкойB, должно быть значением, сохраненным некоторым побочным эффектом,A который модифицируетсяM, чегоB не было раньшеA. [ Note: Набор таких побочных эффектов также ограничен остальными правилами, описанными здесь, и, в частности, требованиями согласованности ниже. ]end note

Если операцияA , изменяющая атомарный объект,M происходит до операции,B которая изменяетM, то онаA должна быть раньше, чемB в порядке модификацииM. [ Note: Это требование известно как согласованность записи-записи. ]end note

Если значение вычислениеA атомного объектаM происходит до вычисления значенияB изM, иA принимает значение от побочного эффектаX наM, то значение вычисляется путемB должно быть либо значение хранитсяX или значение , сохраненное с помощью побочного эффектаY на M, гдеY следуетX в порядок модификацииM. [ Note: Это требование известно как согласованность чтения-чтения. ]end note

Если вычислениеA значения атомарного объектаM происходит до операции,B которая модифицируетM, тогдаA оно должно принимать свое значение из побочного эффектаX наM, гдеX предшествуетB в порядке модификацииM. [ Note: Это требование известно как согласованность чтения-записи. ]end note

Если побочный эффектX на атомном объектеM происходит до вычисления значенияB изM, то оценкаB принимает свое значение отX или от побочного эффекта ,Y который следуетX в порядке модификацииM. [ Note: Это требование известно как согласованность записи-чтения. ]end note

[ Note: Четыре предшествующих требования согласованности эффективно запрещают компилятор переупорядочивать атомарные операции для одного объекта, даже если обе операции являются ослабленными нагрузками. Это фактически обеспечивает гарантию согласованности кэша, предоставляемую большинством оборудования, доступного для атомарных операций C ++. ]end note

[ Note: Значение, наблюдаемое при загрузке атома, зависит от отношения «произошло раньше», которое зависит от значений, наблюдаемых при загрузке атома. Предполагаемое прочтение состоит в том, что должна существовать ассоциация атомарных нагрузок с модификациями, которые они наблюдают, что вместе с соответствующим образом выбранными порядками модификации и отношением «происходит до», полученным, как описано выше, удовлетворяют результирующим ограничениям, как наложено здесь. ]end note

Два действия -potentially concurrent если

  • они выполняются разными потоками, или

  • они не упорядочены, по крайней мере один из них выполняется обработчиком сигнала, и оба они не выполняются одним и тем же вызовом обработчика сигналов.

Выполнение программы содержит,data race если она содержит два потенциально одновременных конфликтующих действия, по крайней мере одно из которых не является атомарным, и ни одно из них не происходит раньше другого, за исключением особого случая для обработчиков сигналов, описанного ниже. Любая такая гонка данных приводит к неопределенному поведению. [ Note: Можно показать, что программы, которые правильно используют мьютексы иmemory_­order_­seq_­cst операции для предотвращения всех гонок данных и не используют никаких других операций синхронизации, ведут себя так, как если бы операции, выполняемые составляющими их потоками, просто чередовались, при этом каждое вычисление значения объекта берется из последнего побочный эффект на этот объект в этом чередовании. Обычно это называют «последовательной согласованностью». Однако это относится только к программам без гонки данных, а программы без гонки данных не могут наблюдать большинство программных преобразований, которые не изменяют семантику однопоточной программы. Фактически, большинство однопоточных программных преобразований по-прежнему разрешены, поскольку любая программа, которая в результате ведет себя иначе, должна выполнять неопределенную операцию. ]end note

Два доступа к одному и тому же объекту типаvolatile std​::​sig_­atomic_­t не приводят к гонке данных, если оба происходят в одном потоке, даже если один или несколько происходит в обработчике сигнала. Для каждого вызова обработчика сигнала оценки, выполняемые потоком, вызывающим обработчик сигнала, могут быть разделены на две группы,A иB, таким образом, никакие оценки не B происходят до оценок вA, и оценки такихvolatile std​::​sig_­atomic_­t объектов принимают значения, как если бы все оценки вA произошли до выполнения. обработчика сигнала и выполнение обработчика сигнала произошло до всех вычислений вB.

[ Note: Преобразования компилятора, которые вводят назначения для потенциально разделяемой области памяти, которая не может быть изменена абстрактной машиной, как правило, запрещены настоящим международным стандартом, поскольку такое назначение может перезаписать другое назначение другим потоком в случаях, когда выполнение абстрактной машины не сталкивались с гонкой данных. Это включает в себя реализации назначения элементов данных, которые перезаписывают соседние элементы в отдельных ячейках памяти. Изменение порядка атомных нагрузок в случаях, когда рассматриваемые атомы могут быть псевдонимами, также обычно не допускается, поскольку это может нарушать правила согласованности. ]end note

[ Note: Преобразования, которые вводят спекулятивное чтение потенциально разделяемой области памяти, могут не сохранять семантику программы C ++, как определено в этом международном стандарте, поскольку они потенциально вызывают гонку данных. Однако они обычно действительны в контексте оптимизирующего компилятора, который нацелен на конкретную машину с четко определенной семантикой для гонок данных. Они были бы недействительны для гипотетической машины, которая не терпит гонок или обеспечивает обнаружение гонок на оборудовании. ]end note