Должен ли 64-битный метод Compare&Swap (CAS) работать на 32-битной машине? (или 64-битная машина?)

Итак, я прочитал, что на 32-битной машине можно использовать операцию CAS с выровненными 64-битными блоками. Точно так же на 64-битной машине можно использовать операцию CAS с выровненными 128-битными блоками.

Я использую 32-битную машину, поэтому я попробовал следующее:

// sizeof(long long) is 8 bytes, so 64 bits
long long y = 12;
long long z = 12;
long long x = 99;
__sync_bool_compare_and_swap(&y, z, x);

и CAS удалось изменить значение y на 99.

Но затем я попытался использовать char array[8]; (размер которого составляет 64 бита) вместо long long. И я делаю:

char full[8] = {'0', '1', '2', '3', '4', '5', '6', '7'}; 
char full2[8] = {'0', '1', '2', '3', '4', '5', '6', '7'};   
char full3[8] = {'5', '8', '9', 'G', 'X', '5', '6', 'U'};
__sync_bool_compare_and_swap(full, full2, full3);  

Но в этом случае CAS терпит неудачу, хотя full и full2 имеют точно такие же данные. (Я также проверил, что full и full2 правильно выровнены)

Итак, в первый раз кажется, что CAS можно использовать для 64-битной, но во второй раз кажется, что это невозможно. Есть идеи, почему?

ИЗМЕНИТЬ

(Как насчет 64-битных машин?)

Итак, проблема была в том, что я использовал char * в своем CAS, а они были только проверены. Таким образом, решение состояло в том, чтобы привести к long long или uint64_t, которые являются 64-битными значениями.

Но что мне делать с 64-битной машиной, когда мне нужно использовать 128-битное значение? long long по-прежнему 64-битный на 64-битной машине, а uint128_t, похоже, не существует в C. Так к какому типу мне следует привести? double long кажется 128-битным на моей 64-битной машине, но при выполнении следующих действий:

  double long y = 32432143243214;
  double long z = 32432143243214;

  int x = __sync_bool_compare_and_swap(&y, z, 1234321990);

Я получаю эту ошибку компиляции error: incompatible type for argument 1 of ‘__sync_bool_compare_and_swap’.


person Fooko R.    schedule 13.02.2012    source источник
comment
Похоже, вы распадаетесь на указатель и не передаете значение   -  person David Heffernan    schedule 13.02.2012


Ответы (3)


Похоже, вы забыли удалить указатели и выполнить кастинг.

Я проверил, и это единственная правильная комбинация:

__sync_bool_compare_and_swap((long long*)full, *(long long *)full2, *(long long *)full3);

Вам нужно указать первый параметр, иначе он заменит только первый символ.

Что касается обработки 128-битной двойной длины, это из gcc 4.1.2 документы.

Определение, данное в документации Intel, допускает использование только типов int, long, long long, а также их беззнаковых аналогов. GCC допускает любой целочисленный скаляр или тип указателя длиной 1, 2, 4 или 8 байтов.

Таким образом, кажется, что вы не можете использовать эту функцию для обработки этого случая.

person Michael Chinen    schedule 13.02.2012
comment
Вы можете передать имя массива как адрес - вы не должны использовать его для этого. (Размер разных типов указателей может отличаться, но мы уже промахнулись здесь с UB) - person asaelr; 13.02.2012
comment
Компилятор также проверяет типы указателей, и эта функция перегружена для нескольких типов указателей, поэтому здесь это имеет значение (например, f(int*) и f(char*) будут вызывать разные функции в зависимости от приведения). Я попробовал ваше решение, и оно поменяло местами только первый символ. - person Michael Chinen; 13.02.2012
comment
Спасибо. Я не думал об этой перегрузке. - person asaelr; 13.02.2012
comment
Спасибо за ответ (еще раз), не могли бы вы проверить мое редактирование (в вопросе)? - person Fooko R.; 13.02.2012

Вы должны передать значение full2 и full3, а не указатель на него. Также следует позаботиться о выравнивании.

__sync_bool_compare_and_swap((long long*)full,*(long long*)full2,*(long long*)full3);

(Конечно, это не переносимо. Если вам нужна переносимость, используйте uint64_t вместо long long)

person asaelr    schedule 13.02.2012
comment
__sync_bool_compare_and_swap/CMPXCHG*B сам по себе не является чем-то переносимым. - person Alexey Frunze; 13.02.2012
comment
Я проверил это, и это дает неправильный результат: 5, 1, 2, 3, 4, 5, 6, 7, - person Michael Chinen; 13.02.2012
comment
Спасибо за ответ, не могли бы вы проверить мое редактирование (в вопросе)? - person Fooko R.; 13.02.2012
comment
Поскольку вы изменили свой ответ, чтобы он соответствовал моему, не могли бы вы добавить что-то оригинальное или удалить его, чтобы избежать шума? Ради комментария исходный ответ, который вы опубликовали, был __sync_bool_compare_and_swap(full,(long long)full2,(long long)full3) без приведения первого указателя. - person Michael Chinen; 13.02.2012
comment
Да, но мой первоначальный комментарий, который пользователи видят перед историей редактирования, без моего второго комментария сбил бы с толку. - person Michael Chinen; 13.02.2012

Вы передаете char * в __sync_bool_compare_and_swap. Предполагая, что ваши массивы символов (все три!) правильно выровнены по 64 битам (если они распределены так, как вы показываете, они могут быть не такими - используйте malloc!), попробуйте выполнить приведение к (long long *) перед переходом к __sync_bool_compare_and_swap. В противном случае используйте встроенный ассемблер и вызовите CMPXCHG8B напрямую.

person bdonlan    schedule 13.02.2012
comment
Спасибо за ответ, не могли бы вы проверить мое редактирование (в вопросе)? - person Fooko R.; 13.02.2012
comment
@FookoR., откройте новый вопрос для 128-битного вопроса CAS, у него совсем другой ответ (киньте мне ссылку на него, как только вы это сделаете) - person bdonlan; 14.02.2012