Compare_exchange_strong не работает, несмотря на то, что данные соответствуют ожидаемому значению

Проблема заключается в том, что compare_exchange_strong возвращает false, несмотря на то, что базовые данные равны expected. например.:

std::atomic<data> ptr;
...
auto ptr_data = ptr.load();
bool cmp_result = memcmp(&ptr_data, &expected, sizeof(ptr_data));
bool cas_result = ptr.compare_exchange_strong(expected, desired);
assert(cas_result || !cmp_result);

data — это 128-битный POD. ptr.is_lock_free() возвращает истину. Это тестируется в однопоточном режиме. cas_result всегда ложно, cmp_results всегда верно.

Компиляция выполняется с помощью компилятора Intel C++, версия 16, обновление 2. В Linux используется libstdc++ версии 5.3.1. 64-битный двоичный файл.
Точно такой же код правильно работал при компиляции в Windows, с тем же ICC16, но как 32-битный код. Это заставляет меня поверить, что это причуда реализации stdlib.

Спасибо


person unexpectedvalue    schedule 21.02.2016    source источник
comment
Это мой недостаток, пожалуйста, не обращайте на это внимания. Пример написан по памяти. Значения сравнивались отладчиком перед CAS. Обновлено.   -  person unexpectedvalue    schedule 21.02.2016
comment
Хмм хорошо. Реализация библиотеки просто зависит от встроенного компилятора, __atomic_compare_exchange, поэтому я сомневаюсь, что это проблема библиотеки.   -  person T.C.    schedule 21.02.2016
comment
Я думаю, что вы вызываете memcmp неправильно - аргументы уже являются указателями на сравниваемые данные, почему вы передаете их как указатели на указатели?   -  person David Haim    schedule 06.07.2020


Ответы (1)


У меня была такая ситуация и в структуре, которую я использовал. Я думаю, что это было вызвано битами заполнения, вставленными из-за проблем с выравниванием, и тем фактом, что compare_exchange выполняет побитовое сравнение двух значений.

На моей машине размер слова/указателя составляет 8 байт, а структура, которая у меня была, была примерно такой:

struct s {
    int i;
    void *ptr;

};

который использовал только 12 байтов для представления своего содержимого (ptr был 8 байтов, int был 4), но его размер составлял 16 байтов (sizeof(s) == 16). Что я сделал, так это изменил структуру на:

struct s {
    long long i;
    void *ptr;
};

Поскольку на моей машине long long составляет 8 байт, эта версия имела размер 16 байт, и все они использовались для представления фактического значения (без битов заполнения).

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

person Burcea Bogdan Madalin    schedule 22.06.2020
comment
memcmp предполагается также является побитовым сравнением полного представления объекта, включая заполнение, я думаю. Но я предполагаю, что проблема в том, что ICC оптимизирует memcmp, просто сравнивая значения на равенство. Предложение: используйте intptr_t или ptrdiff_t, чтобы получить int размером с указатель для 32- или 64-битного режима. Таким образом, это всегда два члена одинакового размера, и их размер может составлять всего 8 байт, если вы когда-либо компилируете как 32-битный или 64-битный ILP32 ABI, такой как Linux x32 или ILP32 AArch64. - person Peter Cordes; 22.06.2020