我在项目 libuv 1.3.0 中找到了这些代码。但我不明白为什么需要内存屏障(对于编译器)。
static int uv__async_make_pending(int* pending) {
/* Do a cheap read first. */
if (ACCESS_ONCE(int, *pending) != 0)
return 1;
/* Micro-optimization: use atomic memory operations to detect if we've been
* preempted by another thread and don't have to make an expensive syscall.
* This speeds up the heavily contended case by about 1-2% and has little
* if any impact on the non-contended case.
*
* Use XCHG instead of the CMPXCHG that __sync_val_compare_and_swap() emits
* on x86, it's about 4x faster. It probably makes zero difference in the
* grand scheme of things but I'm OCD enough not to let this one pass.
*/
#if defined(__i386__) || defined(__x86_64__)
{
unsigned int val = 1;
__asm__ __volatile__ ("xchgl %0, %1"
: "+r" (val)
: "m" (*pending));
return val != 0;
}
#elif defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ > 0)
return __sync_val_compare_and_swap(pending, 0, 1) != 0;
#else
ACCESS_ONCE(int, *pending) = 1;
return 0;
#endif
}
我们可以通过不同的环境来简化这些代码。对于 x86:
static int uv__async_make_pending(int* pending) {
if (ACCESS_ONCE(int, *pending) != 0)
return 1;
{
unsigned int val = 1;
__asm__ __volatile__ ("xchgl %0, %1"
: "+r" (val)
: "m" (*pending));
return val != 0;
}
}
对于 GNU C 4:
static int uv__async_make_pending(int* pending) {
if (ACCESS_ONCE(int, *pending) != 0)
return 1;
return __sync_val_compare_and_swap(pending, 0, 1) != 0;
}
对于其他人:
static int uv__async_make_pending(int* pending) {
if (ACCESS_ONCE(int, *pending) != 0)
return 1;
ACCESS_ONCE(int, *pending) = 1;
return 0;
}
我以最后一个为例。如果我消除它的记忆障碍:
static int uv__async_make_pending(int* pending) {
if (*pending != 0)
return 1;
*pending = 1;
return 0;
}
上面的代码用gcc编译后会不会出错?我不知道gcc会做什么优化。
我直接google了代码,但是没有结果来解释为什么需要内存屏障。
我google了内存屏障,但是没有结果来解释我何时需要对特定代码使用内存屏障。我了解什么是记忆障碍。
我有goole的C语言无锁编程,但是没有结果来解释编译器如何优化代码。
@Jérôme Richard,@Peter Cordes 感谢您的意见。我想我已经弄清楚了这个问题。这里的ACCESS_ONE(内存屏障)保证对挂起的变量进行内存读取或写入,而不是寄存器。 因为我把全部精力都放在指令记录上,而忽略了寄存器替代内存访问的优化。在错误的方向上越走越远。当另一个人陷入无限循环时,有人给出简单的提示是很重要的。