为什么 g++ O2 选项使无符号环绕不起作用?

Why g++ O2 option make unsigned wrap around not working?

我试图用 C++ 编写一个队列,我从 intel dpdk libring 了解到我可以通过使用无符号环绕 属性:

编写类似的代码来做到这一点
#include <cstdio>
#include <cassert>
#include <atomic>
#include <thread>

size_t global_r = 0, global_w = 0, mask_ = 3;

void emplace() {
  unsigned long local_w, local_r, free_entries = 0;
  local_w = global_w;
  while (free_entries == 0) {
    local_r = global_r;
    free_entries = (mask_ + local_r - local_w);
  }
  fprintf(stderr, "%lu\n", free_entries);
  auto w_next = local_w + 1;
  std::atomic_thread_fence(std::memory_order_release);
  global_w = w_next;
}

void pop() {
  unsigned long local_r = global_r;
  unsigned long r_next = local_r + 1;
  // make sure nobody can write to it before destruction
  std::atomic_thread_fence(std::memory_order_release);
  global_r = r_next;
}

int main() {
  std::jthread([]() -> void {
    int i = 10;
    while (i-- >= 0) emplace();
  });
  std::jthread([]() -> void {
    int i = 10;
    while (i-- >= 0) pop();
  });
  return 0;
}

当我用 g++ O0 和 O2 运行 时,它会产生不同的结果: 使用 O2:

3
2
1
0
18446744073709551615
18446744073709551614
18446744073709551613
18446744073709551612
18446744073709551611
18446744073709551610
18446744073709551609

无 O2:

3
2
1
.....long time suspending

请问我对unsinged wrap around的理解有没有错? (我从几个 Whosebug post 和其他参考资料中了解到未标记的环绕是定义的行为)。

您是否知道一旦 global_w 递增到 3 然后 emplace() 中的 while 循环变成无限循环? AFAIK,无限循环导致 C++ 中的未定义行为

我认为您的问题是因为您将 std::jthread 对象定义为临时对象。这意味着它们在它们出现的表达式末尾被破坏。因此,两个线程不会 运行 并行(同时)。

您可以通过创建线程变量轻松更改它,这些变量将在 main():

结束时销毁
int main()
{
  std::thread t1 ([]() -> void {  // note that "t1" variable name
    int i = 10;
    while (i-- >= 0) emplace();
  });

  std::thread t2 ([]() -> void {  // note that "t2" variable name
    int i = 10;
    while (i-- >= 0) pop();
  });
}

然而,即便如此,我认为您在 global_r 上存在 数据竞争 ,这会导致 未定义的行为 作为出色地。如果没有它的同步写入,编译器可能很容易假设 emplace() 具有对 global_r 的独占访问权,并有效地从循环中“删除”此读取 local_r = global_r;

此类问题的现场演示:https://godbolt.org/z/566WP9n36