Реализация атомарного сравнения и обмена

#c #gcc #locking #atomic #inline-assembly

#c #gcc #блокировка #атомарный #встроенная сборка

Вопрос:

У меня есть следующий фрагмент кода для сравнения и обмена

  18     static size_t compareAndExchange( volatile size_t* addr, size_t oldval, size_t newval )
 19     {
 20       size_t ret;
 21       __asm__ volatile( "lock cmpxchgq %2, %1nt"
 22                     :"=a"(ret), " m"(*addr)
 23                     : "r"(newval), "0"(oldval)
 24                     : "memory" );
 25       return ret;
 26     }
  

Ниже приведен тест для сравнения и обмена

   4 struct node
  5 {
  6     struct node* next;
  7     int locked;
  8
  9     node():next( 0 ), locked( false ) {}
 10 };
 11
 12 int main()
 13 {
 14     struct node* head = 0;
 15     struct node* temp = new struct node;
 16
 17     struct node* ret = reinterpret_cast< struct node* > (
 18                           AtomicUtils::compareAndExchange(
 19                             reinterpret_cast< volatile size_t*>( amp;head ),
 20                             0,
 21                             reinterpret_cast< size_t >( temp )
 22                           )
 23                        ) ;
 24
 25     std::cout<< "nExpected: Ret = " << 0 << "Found: " << 0 << std::endl;
 26     std::cout<< "nExpected: head = " << temp << "Found: " << head << std::endl;
 27 }
  

Вопросы:

1) В настоящее время, чтобы атомарно установить значение head, я обманываю, преобразуя указатель size_t ** в указатель size_t * с помощью reinterpret_cast . Это выглядит некрасиво. Есть ли лучший способ (возможно, с помощью лучшей реализации встроенной сборки) для достижения того же эффекта?

2) Зачем нам нужен квалификатор volatile для аргумента?

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

1. Можете ли вы использовать std::atomic из C 11?

2. В моей компании у нас нет версии gcc, которая поддерживает c 11

3. Этот код вообще работает? Похоже, вы никогда не устанавливаете накопитель…

4. @KerrekSB: cmpxcgh выдает «результат» в EAX.

5. @MatsPetersson: Он также ожидает «ввода» в EAX…

Ответ №1:

В C вы могли бы использовать шаблоны:

 template<typename T>
static T compareAndExchange( volatile T* addr, T oldval, T newval )
{
  .... rest of code should remain the same ... 
}
  

И ваш тестовый код теперь будет:

  node* ret = AtomicUtils::compareAndExchange(amp;head, 0, temp);
  

(Я так понимаю, вы знаете о std::atomic и его членах, и вы просто делаете это в качестве учебного упражнения [я вижу, по-видимому, другие причины не использовать это])

gcc также поддерживает встроенные атомарные функции (то есть функции, которые компилятор может напрямую транслировать в машинный код), одной из которых является __sync_bool_compare_and_swap .

Смотрите больше здесь:https://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Atomic-Builtins.html

Редактировать: вы бы заметили, если что-то пойдет не так, если вы измените это:

  std::cout<< "nExpected: Ret = " << 0 << "Found: " << 0 << std::endl;
  

Для:

  std::cout<< "nExpected: Ret = " << 0 << "Found: " << ret << std::endl;
  

Edit2: Что касается вашего второго вопроса, о volatile : вам это не нужно для вашей функции подкачки как таковой [кроме самой __asm__ , поскольку это не позволяет компилятору оптимизировать ее и потенциально изменять порядок инструкций и т.д.]. Вам это действительно нужно, если вы когда-либо собираетесь использовать volatile T* аргумент для самой функции swap, или вам нужно const_cast удалить volatile атрибут.

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

1. Я использую clang. Итак, я не могу использовать атомарику gcc.

2. @KodeWarrior: я был почти уверен, что clang совместим с g в этом контексте, но если у вас достаточно новый clang, вы сможете использовать clang.llvm.org/docs/LanguageExtensions.html#langext-c11-atomic

3. Спасибо за указатель. Ценю это! В настоящее время я пытаюсь поиграть со встроенной сборкой в качестве учебного упражнения.

4. Я это понимаю. Просто он намного менее переносим (например, если вы скомпилируете с -m32, ваш код не будет работать. Встроенные средства позаботятся об этом (а также, например, поддерживают произвольные типы указателей).

5. да, то, что вы говорите, определенно имеет смысл. Спасибо.