[英]Do I use atomic<> over shared memory correctly here
Suppose we have two processes.假设我们有两个进程。
Both use same mmap-ed memory region in order to pass some information, in this case uint16_t
.两者都使用相同的 mmap-ed memory 区域来传递一些信息,在本例中为
uint16_t
。
I read all kind of opinions why std::atomic<>
should work, if it uses is_always_lock_free
.我阅读了各种意见,如果
std::atomic<>
使用is_always_lock_free
,它应该起作用。
So I did this code.所以我做了这个代码。 Code works as expected, but is this code thread / inter-process safe:
代码按预期工作,但此代码线程/进程间安全吗:
#include <cstdint>
#include <atomic>
#include <iostream>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h> // sleep
#include <time.h>
using I = uint16_t;
using AI = std::atomic<I>;
static_assert(std::atomic<I>::is_always_lock_free);
constexpr size_t SIZE = sizeof(AI);
constexpr const char *NAME = "nmmm";
constexpr I STOP = 12345;
void error(const char *msg){
std::cout << msg << '\n';
exit(10);
}
int main(int argc, const char **argv){
std::cout << sizeof(I) << ' ' << sizeof(AI) << '\n';
int fd = shm_open(NAME, O_RDWR | O_CREAT, 0644);
if (fd < 0)
error("shm_open");
int t = ftruncate(fd, SIZE);
if (t < 0)
error("ftruncate");
void *vmem = mmap(nullptr, SIZE, PROT_WRITE, MAP_SHARED, fd, 0);
if (vmem == MAP_FAILED)
error("mmap");
std::cout << "All set up!" << ' ' << vmem << '\n';
AI *ai = reinterpret_cast<AI *>(vmem);
if (argc > 1){
switch(argv[1][0]){
case 'g':
case 'G':
ai->store(0, std::memory_order_relaxed);
while(true){
auto x = ai->load(std::memory_order_relaxed);
std::cout << x << '\n';
if (x == STOP)
break;
sleep(1);
}
case 's':
case 'S':
ai->store(STOP, std::memory_order_relaxed);
break;
default:
{
srand(time(nullptr));
I const x = rand() & 0xFFFF;
std::cout << "set val to " << x << '\n';
ai->store(x , std::memory_order_relaxed);
}
break;
}
}
munmap(vmem, SIZE);
// shm_unlink(NAME);
}
Excuse my mixing of C and C++ like using rand
.请原谅我像使用
rand
一样混合 C 和 C++ 。
What you suppose to do it -你想做什么——
# console 1
./a.out g # start "Getter"
# console 2
./a.out x # set random value
# or
./a.out s # set "Stop" value, so process in console 1 stops.
What I am worrying:我担心的是:
is_always_lock_free
?is_always_lock_free
总是这样吗? Is std::atomic
just a fancy Assembly instruction? std::atomic
只是一个花哨的汇编指令吗? Update更新
It appears, std::atomic<uint16_t>
is POD.看来,
std::atomic<uint16_t>
是 POD。 So casting is not UB (undefined behavour)所以铸造不是UB(未定义的行为)
static_assert(std::is_pod_v<std::atomic<uint16_t> >);
Also it appears std::atomic<uint16_t>
operations compiles to single assembly instructions:似乎
std::atomic<uint16_t>
操作编译为单个汇编指令:
https://gcc.godbolt.org/z/d6bK9jfza https://gcc.godbolt.org/z/d6bK9jfza
What I am worrying:
我担心的是:
- no placement new, just reinterpret cast + initial value set.
没有新的位置,只是重新解释演员 + 初始值集。
- not a d-tor either.
也不是 d-tor。
- why sizeof(uint16_t) is same as sizeof(std::atomic<uint16_t>) - is it always like this if is_always_lock_free?
为什么 sizeof(uint16_t) 与 sizeof(std::atomic<uint16_t>) 相同 - 如果 is_always_lock_free 总是这样吗? Is std::atomic just a fancy Assembly instruction?
std::atomic 只是一个花哨的汇编指令吗?
These assumtions are likely to be true, but formally these are leading to UB.这些假设可能是正确的,但从形式上讲,这些假设导致了 UB。
To avoid running into UB, use C++20 atomic_ref
.为避免遇到 UB,请使用 C++20
atomic_ref
。 Share plain uint16_t
and use atomic_ref
to it in processes.共享普通的
uint16_t
并在进程中使用atomic_ref
。
With atomic_ref
it is still not formally guaranteed to work in an interprocess way (no interprocess in the standard C++ at all), but you will not be running into the mentioned UB, so no concerns about lifetime or incorrect aliasing.使用
atomic_ref
仍然不能正式保证以进程间方式工作(标准 C++ 中根本没有进程间),但您不会遇到提到的 UB,因此无需担心生命周期或不正确的别名。
Note that you have to keep is_always_lock_free
assertion.请注意,您必须保留
is_always_lock_free
断言。 Without it, atomic_ref
would use a locking mechanism to provide atomic semantic, this mechanism is likely to be private to a program / process.没有它,
atomic_ref
将使用锁定机制来提供原子语义,这种机制很可能是程序/进程私有的。
Because C++ atomic
are not very "clear" to use, you can use stdatomic.h
.因为 C++
atomic
使用起来不是很“清楚”,所以可以使用stdatomic.h
。
https://en.cppreference.com/w/c/atomic/atomic_load https://en.cppreference.com/w/c/atomic/atomic_load
However I failed to compile example with these on gcc.但是我未能在 gcc 上用这些编译示例。
This is why I decided to use gcc builtin atomics:这就是为什么我决定使用 gcc 内置原子:
https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html
These works on both gcc
and clang
on Linux.这些适用于 Linux 上的
gcc
和clang
。 Did not checked on MacOS yes.没有在 MacOS 上检查是的。
Here is the code, almost similar to what I posted before.这是代码,与我之前发布的代码几乎相似。 Once again excuse C style code.
再次原谅 C 样式代码。
In order code to be similar to what stdatomic.h
offers, I did two functions that call the builtin(s):为了使代码类似于
stdatomic.h
提供的代码,我做了两个调用内置函数的函数:
template<typename T>
void atomic_store(T *a, T val){
return __atomic_store_n(a, val, __ATOMIC_RELAXED);
}
template<typename T>
void atomic_store(T &a, T val){
return atomic_store(& a, val);
}
template<typename T>
T atomic_load(T *a){
return __atomic_load_n(a, __ATOMIC_RELAXED);
}
template<typename T>
T atomic_load(T &a){
return atomic_load(& a);
}
Those suppose to be good with all integral types such int, short, char, unsigned etc.这些假设适用于所有整数类型,例如 int、short、char、unsigned 等。
Here is the full code in case you decide to "dig" future.如果您决定“挖掘”未来,这是完整的代码。
#include <cstdio>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h> // sleep
#include <cstdlib> // exit
#include <time.h>
using MyAtomicInt = int;
constexpr size_t SIZE = sizeof(MyAtomicInt);
constexpr const char *NAME = "nmmm";
constexpr MyAtomicInt STOP = 12345;
void error(const char *msg){
printf("%s\n", msg);
exit(10);
}
template<typename T>
void atomic_store(T *a, T val){
return __atomic_store_n(a, val, __ATOMIC_RELAXED);
}
template<typename T>
void atomic_store(T &a, T val){
return atomic_store(& a, val);
}
template<typename T>
T atomic_load(T *a){
return __atomic_load_n(a, __ATOMIC_RELAXED);
}
template<typename T>
T atomic_load(T &a){
return atomic_load(& a);
}
int main(int argc, const char **argv){
int fd = shm_open(NAME, O_RDWR | O_CREAT, 0644);
if (fd < 0)
error("shm_open");
int t = ftruncate(fd, SIZE);
if (t < 0)
error("ftruncate");
void *vmem = mmap(nullptr, SIZE, PROT_WRITE, MAP_SHARED, fd, 0);
if (vmem == MAP_FAILED)
error("mmap");
printf("All set up! %p\n", vmem);
MyAtomicInt *ai = reinterpret_cast<MyAtomicInt *>(vmem);
if (argc > 1){
switch(argv[1][0]){
case 'g':
case 'G':
atomic_store(ai, 0);
while(true){
auto x = atomic_load(ai);
printf("%d\n", x);
if (x == STOP)
break;
sleep(1);
}
case 's':
case 'S':
atomic_store(ai, STOP);
break;
default:
{
srand(time(nullptr));
MyAtomicInt const x = rand() & 0xFFFF;
printf("set val to %d\n", x);
atomic_store(ai, x);
}
break;
}
}
munmap(vmem, SIZE);
// shm_unlink(NAME);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.