代码库有一个定义为__asm__ volatile("" ::: "memory")
的COMPILER_BARRIER
宏。宏的目的是防止编译器跨屏障重新排序读写操作。请注意,这是一个明确的编译器障碍,不是处理器级内存障碍。
事实上,这是相当可移植的,因为AssemblerTemplate中没有实际的汇编指令,只有volatile
和memory
clobber。因此,只要编译器遵守GCCs Extended Asm语法,它就应该可以正常工作。尽管如此,如果可能的话,我很好奇在C++11原子API中表达这一点的正确方式是什么。
以下似乎是正确的想法:atomic_signal_fence(memory_order_acq_rel);
。
我的理由是:
- 在
<atomic>
API中,只有atomic_signal_fence
和atomic_thread_fence
不需要针对其进行操作的内存地址 atomic_thread_fence
会影响内存排序,这对于编译器来说是不需要的- Extended Asm版本中的
memory
clobber不区分读取和写入,因此我们似乎同时想要获取和发布语义,因此似乎至少需要memory_order_acq_rel
memory_order_seq_cst
似乎没有必要,因为我们不需要跨线程的总顺序——我们只对当前线程中的指令排序感兴趣
是否可以用C++11原子API完全可移植地表达__asm__ volatile("" ::: "memory")
的等价物?如果是,那么atomic_signal_fence
是正确的API吗?如果是,那么这里需要什么样的内存顺序参数?
或者,我是不是陷入了困境,有更好的方法来解决这个问题?
__asm__ volatile("" ::: "memory")
甚至不是一个完整的编译器障碍;它只强制对asm块可能访问其地址的对象进行加载/存储排序,而asm块不包括编译器可以跟踪其地址不泄漏的局部变量。例如,后面跟有__asm__ volatile("" ::: "memory");
的memset(password, 0, len);
可能无法实际将password[]
使用的存储器归零。
这可以通过将这些对象的地址作为输入传递到asm块来弥补,但我看不到atomic_signal_fence
有任何完美的等价物。最接近的方法可能是将对象的地址存储到外部链接volatile
指针对象中(注意使指针而不是指向类型volatile
限定),然后atomic_signal_fence
必须假设它可能是从信号处理程序访问的。
区分读和写,这样看起来我们想要获取和发布语义
你似乎把不同的问题搞混了。
获取和释放语义都可以对读取和写入产生约束:
- 释放非正式地表示在屏障启动之前,先前的内存操作已经完成
- 非正式地获取意味着在屏障完成之前不会开始以下内存操作
然而,这是一个非常简单的解释C++原子屏障是原子学的屏障。它们与原子物体协同工作。当然,线程屏障调用可以自己生成代码,但可以使用一些非原子操作对代码进行重新排序。