Страницы

Поиск по вопросам

суббота, 14 декабря 2019 г.

Order of volatile access is undefined in this statement

#c #volatile


Имеются две volatile-переменные:

volatile uint32_t a;
volatile uint32_t b;


Они объявлены как volatile, потому что могут измениться как в основной программе,
так и в обработчике прерывания, так и устройствами на системной шине (такими как DMA).
Если обе переменные участвуют в одном выражении, например:

uint32_t c = a + b;


то компилятор выдаёт предупреждение Order of volatile access is undefined in this
statement. Как я понимаю, это означает, что неизвестно, какая переменная первой загрузится
в регистр общего назначения, и возможна такая ситуация, когда переменная a загрузилась
в регистр, сработало прерывание, изменило это переменную, а основная программа продолжает
работать со старым значением. Некоторые источники рекомендую в таких ситуациях разбивать
выражение на части, например:

uint32_t c = a;
c += b;


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

А теперь собственно вопрос. В программе обработчики прерываний постоянно периодически
изменяют volatile-переменные, а основная программа выполняет математические преобразования.
Как только пришло новое значение переменной, старое уже неинтересно. Можно ли в этом
случае игнорировать предупреждение компилятора? Есть ли у этого предупреждения другие
источники, о которых я не догадываюсь? 
    


Ответы

Ответ 1



В языке С порядок доступа к volatile объектам, наряду с вызовами функций ввода-вывода, является частью наблюдаемого поведения (observable behavior) программы. Поэтому данное предупреждение просто выглядит как предупреждение от педантичного компилятора, который говорит, что наблюдаемое поведение программы в данном случае однозначно не определено. Компилятор прав - оно действительно не определено однозначно. Но компилятор, разумеется, не знает, какие грани наблюдаемого поведения являются действительно важными для специфики вашей программы, а какие нет, и выдает абстрактное педантичное предупреждение. Если в данном случае для вас порядок доступа к этим переменным действительно важен, тогда предупреждение вполне оправданно. Если же вы не видите проблем с любым возможным порядком доступа, то перепишите код чисто ради устранения предупреждения от компилятора. Понятно, что в описанной вами ситуации, при модификации a и b из независимых линий исполнения (потоки, прерывания и т.п.) и при отсутствии какой-либо синхронизации, будет наблюдаться выраженный data race. А уж смертелен ли этот data race для вашего приложения - вам виднее. Если вы захотите избавиться от этого data race, то тут одним подавлением предупреждений не обойдешься - придется организовывать ту или иную форму синхронизации доступа к этой паре.

Ответ 2



"но на мой взгляд, так мы лишь затыкаем компилятор, а не устраняем причину проблемы" Устранить причину проблемы в рамках языка программирования и компилятора вы не сможете в принципе (на то и ключевое слово volatile), так что просто примите это предупреждение к сведению и сами решайте, насколько оно важно в конкретной задаче...

Ответ 3



Как только пришло новое значение переменной, старое уже неинтересно. У меня была аналогичная проблема. И я решал её приблизительно так: volatile uint32_t a; volatile uint32_t b; uint32_t a1; uint32_t b1; uint32_t x uint8_t trust; // Запоминаем исходные значения a1=a; b1=b; // Выполняем расчёт нужного значения x = a1+b1; // Проверяем, можно ли доверять этому значению if ( (a == a1) && (b == b1) ) trust = 1; else trust = 0; Я понимаю, что с точки зрения теории, это решение - далеко не идеальное. Понимаю, что будут ложные присвоения trust, но если достаточно только уверенности в том. что результат расчёта действительно соответствует ПАРЕ исходных значений, то это почти наверняка. Не 100%, но такая гарантия лучше никакой. А вот если trust==0, то это почти на 100% защитит от ложных результатов.

Комментариев нет:

Отправить комментарий