c++ - 将 std::atomic 与 futex 系统调用一起使用

标签 c++ linux c++20 stdatomic futex

在 C++20 中,我们可以在原子变量上休眠,等待它们的值改变。
我们使用 std::atomic::wait 方法。
不幸的是,虽然 wait已标准化,wait_forwait_until不是。这意味着我们不能在超时的原子变量上休眠。
无论如何,使用 WaitOnAddress 在幕后实现在原子变量上休眠在 Windows 和 futex 上Linux 上的系统调用。
解决上述问题(无法在具有超时的原子变量上休眠),我可以传递 std::atomic 的内存地址至WaitOnAddress在 Windows 上,它将(有点)在没有 UB 的情况下工作,因为函数获取 void*作为参数,投std::atomic<type>有效至void*在Linux上,是否可以混std::atomic不清楚与 futex . futex得到一个 uint32_t*int32_t* (取决于您阅读的手册),并转换 std::atomic<u/int>u/int*是UB。另一方面,手册说

The uaddr argument points to the futex word. On all platforms, futexes are four-byte integers that must be aligned on a four- byte boundary. The operation to perform on the futex is specified in the futex_op argument; val is a value whose meaning and purpose depends on futex_op.


暗示 alignas(4) std::atomic<int>应该可以工作,只要类型的大小为 4 字节且对齐为 4,它是哪种整数类型都没有关系。
此外,我还看到很多地方实现了这种结合 atomics 和 futexes 的技巧,包括 boostTBB .
那么以非 UB 方式在具有超时的原子变量上 sleep 的最佳方法是什么?
我们是否必须使用操作系统原语实现我们自己的原子类才能正确实现它?
(存在混合原子和条件变量等解决方案,但不是最佳的)

最佳答案

您不必一定要实现完全自定义的 atomic API,从 atomic<T> 中简单地拉出指向基础数据的指针实际上应该是安全的。并将其传递给系统。
由于std::atomic不提供 native_handle 的等价物与其他同步原语提供的一样,您将被困在做一些特定于实现的黑客攻击以尝试使其与 native API 接口(interface)。
在大多数情况下,假设实现中这些类型的第一个成员与 T 相同是相当安全的。类型——至少对于整数值 [1]。这是一种保证,可以提取该值。

... and casting std::atomic<u/int> to u/int* is UB


事实并非如此。
std::atomic 由标准保证为Standard-Layout Type .标准布局类型的一个有用但通常深奥的属性是它是安全的 reinterpret_cast一个 T to a value or reference of the first sub-object (例如 std::atomic 的第一个成员)。
只要我们能保证std::atomic<u/int>仅包含 u/int作为一个成员(或者至少作为它的第一个成员),那么以这种方式提取类型是完全安全的:
auto* r = reinterpret_cast<std::uint32_t*>(&atomic);
// Pass to futex API...
这种方法也应该在 windows 上转换 atomic在将其传递给 void* 之前传递给基础类型API。
注:通过 T*指向 void* 的指针被重新解释为 U* (例如 atomic<T>*void* 当它期望 T* 时)是未定义的行为——即使有标准布局保证(据我所知)。它仍然可能会工作,因为编译器无法查看系统 API——但这不会使代码格式正确。
注2:我不能在 WaitOnAddress 上发言API,因为我实际上并没有使用它——但是任何依赖于正确对齐的整数值(void* 或其他)地址的原子 API 都应该通过提取指向基础值的指针来正常工作。

[1] 因为这被标记为 C++20 ,您可以使用 std::is_layout_compatible 进行验证与 static_assert :
static_assert(std::is_layout_compatible_v<int,std::atomic<int>>);
(感谢@apmccartney 在评论中提出的这个建议)。
我可以确认这将与 Microsoft's STL 的布局兼容, libc++ , 和 libstdc++ ;但是,如果您无权访问 is_layout_compatible并且您使用的是不同的系统,您可能需要检查编译器的头文件以确保此假设成立。

关于c++ - 将 std::atomic 与 futex 系统调用一起使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67034029/

相关文章:

c++ - Mac OS OpenGL - CPU 使用问题

c++ - 错误包括 QGeoPositionInfo

linux - 如何仅在端口打开时终止在端口上运行的进程?

linux - wxWidgets 从 linux 交叉编译到 windows - "--host="是什么意思?

linux - 您可以使用 'less' 或 'more' 来输出一页文本吗?

c++ - 64位指针的无锁内存回收

c++ - 使用 std::vector::emplace_back

c++ - 给聚合一个转换构造函数?

c++ - 我可以在不创建语法怪物的情况下转发(类型)通用重载函数吗?

c++ - 在协程调用 `Segmentation fault` 之后,是什么导致此代码 `promise_type::return_value()` ?