libuv中并发C代码的内存屏障必要性及GCC编译优化疑问
关于libuv 1.3.0中并发代码内存屏障的疑问
我在libuv 1.3.0项目中发现一段并发C代码,无法理解为何需要编译器内存屏障,原代码如下:
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; }
核心疑问
以最后一种环境的代码为例,若移除内存屏障(修改为如下代码),经GCC编译后是否会出现错误?我不清楚GCC会执行哪些优化导致问题:
static int uv__async_make_pending(int* pending) { if (*pending != 0) return 1; *pending = 1; return 0; }
另外,我尝试过以下搜索但未找到答案:
- 直接搜索该代码,未找到内存屏障必要性的相关解释
- 了解内存屏障的概念,但搜索内存屏障相关内容,未找到针对此类特定代码的使用场景说明
- 搜索C语言无锁编程相关内容,也未找到编译器对代码优化方式的解释
内容的提问来源于stack exchange,提问作者Droopy
相关产品推荐
相关产品推荐

