代码库的COMPILER_BARRIER
宏定义为__asm__ volatile("" ::: "memory")
。宏的目的是防止编译器重新排序跨障碍的读取和写入。请注意,这显然是编译器屏障,而not是处理器级内存屏障。
原样,这是相当可移植的,因为在AssemblerTemplate中没有实际的组装指令,只有volatile
和memory
遮盖符。因此,只要编译器采用GCC的Extended Asm语法,它就可以正常工作。不过,我很好奇如果可能的话,在C ++ 11原子API中表达这种正确方法的正确方法。
以下似乎是正确的主意:atomic_signal_fence(memory_order_acq_rel);
。
我的理由是:
<atomic>
API中,只有atomic_signal_fence
和atomic_thread_fence
不需要操作的内存地址。atomic_thread_fence
影响内存排序,这对于编译器屏障是不需要的。memory
不区分读写,因此看来我们既要获取语义又要释放语义,因此似乎至少需要memory_order_acq_rel
。memory_order_seq_cst
似乎是不必要的,因为我们不需要跨线程的总顺序-我们只对当前线程内的指令排序感兴趣。是否可以使用C ++ 11原子API完全移植地表示__asm__ volatile("" ::: "memory")
的等价物?如果是,atomic_signal_fence
是否正确使用API?如果是这样,那么这里合适/需要什么存储顺序参数?
或者,我是不是在这里杂草丛生,有更好的方法来解决这个问题?
__asm__ volatile("" ::: "memory")
甚至不是完整的编译器障碍;它只会强制对对象的地址可能可以通过asm块访问的对象进行加载/存储的排序,而对象不包括编译器可以跟踪其地址不会泄漏的局部变量。例如,memset(password, 0, len);
后跟__asm__ volatile("" ::: "memory");
可能无法将password[]
使用的内存实际清零。
这可以通过将这些对象的地址作为输入传递到asm块来解决,但是我看不到atomic_signal_fence
有任何完美的等效项。您可能要做的最接近的操作是将对象的地址存储到外部链接volatile
指针对象中(请小心使指针(而不是指向类型的指针,必须为volatile
限定),然后是atomic_signal_fence
必须假设它可以从信号处理程序访问。
区分读写,所以看起来我们想要获取和释放语义
您似乎正在混淆不同的问题。
获取和释放语义都可以在读写上创建约束:
但是,这是一个非常简单的解释。 C ++原子障碍是原子的障碍。它们与原子对象协同工作。当然,线程屏障调用可以自己产生代码,但是可以使用一些非原子操作对代码进行重新排序。