这些代码(并发C代码)是否必须使用内存屏障?

jvlzgdj9  于 2023-10-15  发布在  其他
关注(0)|答案(1)|浏览(118)

1.我在项目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
}

1.我们可以通过不同的环境来简化这些代码。对于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;
}

1.我用最后一个作为例子。如果我移除它的记忆屏障:

static int uv__async_make_pending(int* pending) {
  if (*pending != 0)
    return 1;

  *pending = 1;
  return 0;
}

上面的代码在gcc编译后会不会做错事?我不知道gcc会做什么优化。
1.我已经谷歌的代码直接,但没有结果来解释为什么内存障碍是必要的。
1.我有谷歌的内存障碍,但没有结果来解释当我需要使用内存障碍的具体代码。我知道什么是记忆障碍。
1.我有古勒的锁免费编程在C语言,但没有结果来解释代码将如何优化编译器。

xcitsw88

xcitsw881#

@Jérôme Richard,@Peter Cordes感谢您的评论。我想我已经知道问题的答案了。这里的ACCESS_ONE(内存屏障)保证从内存中读取或写入变量挂起,而不是寄存器。由于我把注意力都放在了指令的重写上,而忽略了寄存器替代内存访问的优化。在错误的方向上越走越远。当另一个人陷入无限循环时,有人给一个简单的提示是很重要的。

相关问题