我有以下用于比较和交换的代码片段
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限定符?
答案 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
属性,则确实需要它。