Suppose we have two processes.
Both use same mmap-ed memory region in order to pass some information, in this case uint16_t
.
I read all kind of opinions why std::atomic<>
should work, if it uses 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
.
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:
- no placement new, just reinterpret cast + initial value set.
- not a d-tor either.
- why sizeof(uint16_t) is same as sizeof(std::atomic<uint16_t>) - is it always like this if
is_always_lock_free
? Isstd::atomic
just a fancy Assembly instruction?
Update
It appears, std::atomic<uint16_t>
is POD. So casting is not UB (undefined behavour)
static_assert(std::is_pod_v<std::atomic<uint16_t> >);
Also it appears std::atomic<uint16_t>
operations compiles to single assembly instructions: