原子比较和交换实现

Atomic compare and exchange implementation

本文关键字:交换 实现 比较      更新时间:2023-10-16

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

 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*>( &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限定符?

在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属性。