原子比较和交换实施

时间:2014-07-01 22:18:16

标签: c++ gcc locking atomic inline-assembly

我有以下用于比较和交换的代码片段

 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, %1\n\t"
 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*>( &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的值,我是通过reinterpret_cast将size_t **转换为size_t *指针来作弊。这看起来很难看。是否有更好的方法(通过更好的内联汇编实现)来实现相同的效果?

2)为什么我们需要参数的volatile限定符?

1 个答案:

答案 0 :(得分:2)

在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(&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*参数,或者您需要const_cast远离volatile属性,则确实需要它。