В чем причина того, что сигнал пробуждения не может быть потерян при использовании операции RMW по сравнению с чистой заC++

Программы на C++. Форум разработчиков
Ответить
Anonymous
 В чем причина того, что сигнал пробуждения не может быть потерян при использовании операции RMW по сравнению с чистой за

Сообщение Anonymous »

Рассмотрим этот пример:

Код: Выделить всё

#include 
#include 
#include 
extern void block_wait();
extern void wake();

int main(){
std::atomic counter = 0;
std::jthread t1([&](){
if(counter.fetch_sub(1,std::memory_order::relaxed) == 0){  // #0
block_wait(); // #1
}
});
std::jthread t2([&](){
if(counter.fetch_add(1,std::memory_order::relaxed) == -1){ // #2
wake(); // #3
}
});
}
В этом примере Block_wait и Wake не вводят гонку данных, а их функции подразумеваются по их именам. block_wait блокирует поток и ждет сигнала пробуждения, чтобы разблокировать поток. Wake пробуждает заблокированный поток.
Если #0 читает 0, #2 гарантированно прочитает -1 и выполнит пробуждение() для пробуждения #1. Однако если мы изменим #2 на чистую загрузку следующим образом:

Код: Выделить всё

#include 
#include 
#include 
extern void block_wait();
extern void wake();

int main(){
std::atomic counter = 0;
std::jthread t1([&](){
if(counter.fetch_sub(1,std::memory_order::relaxed) == 0){  // #0
block_wait(); // #1
}
});
std::jthread t2([&](){
if(counter.load(std::memory_order::relaxed) == -1){ // #2
wake(); // #3
}
});
}
При том же условии: #0 читает 0 и выполняет #1, чтобы заблокировать поток, в этой ситуации #2 также может читать 0 и не выполняет #3. То есть подход с чистой загрузкой не может гарантировать пробуждение заблокированного потока. Правильность алгоритма не может быть гарантирована при чистой загрузке.
Какова правильная причина, почему #2 в качестве операции RMW не пропускает выполнение Wake() по сравнению с чистой загрузкой с точки зрения стандартного/абстрактного машинного смысла C++? Я пытаюсь дать три объяснения; если объяснение неверно, укажите, почему.
  • Загрузочная часть операции RMW менее склонна к считыванию устаревшего значения, чем чистая загрузка.
Для этого объяснения, как указано в других вопросах, люди считают, что концепция устаревшего значения бесполезна. В любом случае, если эта концепция бесполезна, дайте разумное объяснение, почему этот аргумент неверен.
  • Часть загрузки операции RMW более склонна к чтению более поздней модификации в порядке модификации, чем чистая загрузка.
Аналогично, если этот аргумент неверен, укажите, почему
  • Стандарт C++ накладывает более строгие ограничения на загрузочную часть операции RMW, чем на чистую загрузку.
Я полагаю, что это может быть приемлемым аргументом, помимо правила согласованности, определенного в [intro.races] p11-p14, существует дополнительное правило, определенное в [atomics.order] p10, которое нужно наложить о том, какое значение может читать RMW; вместо этого чистая загрузка не имеет этого ограничения.
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C++»