Я уже давно использую атомарную операцию gcc в многопоточном приложении, а вчера столкнулся с интересным сценарием, который я не могу объяснить.Эти элементарные функции перегружены и могут использовать типы данных шириной 1, 2, 4 или 8 байтов.В частности, я успешно использую операцию bool_compare_and_swap (CAS).Возникающая здесь проблема повторяется и возникает только тогда, когда я компилирую оптимизированный код (O3).Проблема не возникает, когда я компилирую неоптимизированную (O0).Имейте это в виду, так как я считаю, что оптимизатор делает что-то необычное в случае, который я здесь представляю.
Обычно я создаю объединение структуры, содержащей именованные типы (chars, shorts и т. Д.).) и тип данных соответствующего размера, который «вписывается» в эту структуру в один объект (т. е. long long). В этом случае у меня есть 8-байтовый (long long) в объединении, аналог структуры которого содержит битовые поля.Пример определения типа данных показан ниже.Предполагается, что битовые поля могут быть изменены с помощью операторов присваивания, и после того, как все присвоения выполнены, 8-байтовый тип данных будет таким же, как и CAS'd.В частности, я использую:
bool __sync_bool_compare_and_swap (type *ptr, type oldval type newval, ...)
, завернутый в макрос следующим образом:
define THD_CAS(ptr, oldVal, newVal) __sync_bool_compare_and_swap(ptr, oldVal, newVal)
У меня есть структура, определенная следующим образом:
typedef union _TSynchro {
struct {
int *pFirstSynchWork;
unsigned short idTransaction;
unsigned short fNewTrans :1,
fFileBad :1,
fOpComplete :1,
fCancelWork :1,
fPurged :1,
fStatRequired :1;
} Data;
// above struct is overlayed by this struct so we can CAS all values with a single 64 bit cas
long long n64;
} TSynchro;
ИтакУ меня есть цикл параллелизма (while (1)) в коде, который захватывает «снимок» текущего значения данных и сохраняет его в «Old», устанавливает биты в новой копии («New») данных и затем пытается выполнить операцию CAS.Если CAS преуспевает, я - поток, который изменил данные, и я вырываюсь из цикла.Если произошел сбой CAS, какой-то другой поток изменил данные подо мной, и я повторил попытку, захватив еще один «снимок» текущих данных.
void NewSynchro(TSynchro *pSynchro)
{
volatile TSynchro New;
volatile TSynchro Old;
while (1) // concurrency loop
{
Old.n64 = pSynchro->n64;
New.n64 = Old.n64;
New.Data.fOpComplete = 1;
New.Data.fStatRequired = 0;
if (fFileBad)
{
New.Data.fFileBad = 1;
}
else
{
New.Data.fReleased = 0;
New.Data.fFileBad = 0;
}
if (THD_CAS(&pSynchro->n64, Old.n64, New.n64))
break; // success
}
}
Теперь вот что интересно ... Посмотрите, что яобъявляете Старое и Новое изменчивым?Хорошо, если ОБА Старый и Новый не имеют изменчивой модификации, я получаю SEGV, когда перехожу к следующему вызову функции после вызова NewSynchro ().Если EITH OLD или NEW или ОБА имеют модификатор volatile, код приложения никогда не будет SEGV.В разработке я сейчас запускаю только 1 поток (реальной угрозы раздора за изменение значения нет), поэтому я также попытался избавиться от CAS и заменить его простым присваиванием (т. Е. PSynchro-> n64 = New.n64), и приложение тоже работает нормально.
Я использовал 8-байтовый CAS в других местах, и, похоже, он работает нормально.Одно из различий заключается в том, что я думаю, что впервые использую битовые поля в структуре.
Мысли?