I have two processes P1 and P2 implementing IPC using boost::interprocess::managed_shared_memory. Both use the following snippet of code:
namespace ipc = boost::interprocess;
// Open a shared memory segment with given name and size
ipc::managed_shared_memory segment_(ipc::open_or_create, "shared_memory_segment", 1024);
// Immediately proceed to create objects using find_or_construct
auto heartbeat_p1 = segment_.find_or_construct<std::atomic<int64_t>>("heartbeat_p1")(0);
// .. other shared objects
// And then proceed to use them
I think the usage of open_or_create isn't thread-safe here, and does it require an external synchronization?
What might go wrong is either of two cases -
- This is not going to introduce any race conditions. Calls inside open() are going to constitute of OS system calls, which are taken care of by OS (similar to open syscall). Leaving this point for completeness of the question.
Both P1 and P2 enter the code for the constructor of segment_. And since open_or_create is itself a logic that can be represented as:
try {
create();
}
catch {
if (already_exists_error){
try {
open();
}
}
else {
throw();
}
}
We might have a race condition if both try to create() at the same time?
- If P1 is inside open() but hasn't completed making the data structures inside the shared memory that is still needed for the proper functioning of the managed memory segment. And, meanwhile, now P2 is fired, it directly proceeds to open, and attaches to it, a segment which hasn't been completely created. Won't P2 be left with an undefined state for the shared memory object?
I have seen my setup behaving weirdly in which the objects in boost managed shared memory aren't really "shared" across process. And there is no exception being thrown either.
Reference for try-catch block: Is there a better way to check for the existence of a boost shared memory segment?.
c++ Synchronize shared memory when reading also mentions to use external synchronization for reading from a process.
Is my understanding correct that we need external synchronization in using open_or_create from two different processes? And if yes, how do I properly synchronize so that both can use open_or_create?
So I went through the boost source code, and found following observations:
TL;DR : NOPE, we do not need any external synchronization here, since boost source ensures that all calls are synchronized either by OS system calls, or by the atomic reads and compare-and-swap writes into the starting address of the shared memory segment used as an enum.
Longer version: If we sail our way through boost managed_memory_segment source code, all the important bits can be found in this object's constructor:
managed_open_or_create_impl.hpp:
So we have 3 functions of interest here: do_create_else_open(), do_map_after_create(), do_map_after_open().
Let's go through them one-by-one:
Okay, so this is simple, it wraps the try-catch block inside the create_device calls. The create_device expands to shm_open() and is bound to be atomic due to it being an OS system call, similar to file open. So, one process is bound to throw and enters the catch block, where it simply attaches the the OS shared memory already created.
And the one who creates it calls do_map_after_create(). The one who attaches calls do_map_after_open().
do_map_after_create():
Now, do_map_after_open(), broken into 2 parts: Part-1:
In part-1:
Part-2
Thus, all calls are synchronized either by OS system calls, or by the atomic reads and compare-and-swap enum writes into the starting address of the shared memory segment.
Edit: I finally found the issue for which Processes were not able to attach themselves to the same shared memory segment. It was due to systemd deleting the shared memory resources for a user account on logouts. Link: https://superuser.com/a/1179962/1818191