I have 2 processes, process 1 creates a boost managed_shared_memory segment and process 2 opens this segment. Process 1 is then restarted and the start of process 1 has the following,
struct vshm_remove
{
vshm_remove()
{
boost::interprocess::shared_memory_object::remove("VMySharedMemory");
}
~vshm_remove()
{
boost::interprocess::shared_memory_object::remove("VMySharedMemory");
}
} vremover;
I understand that when process 1 starts or ends the remove method will be called on my shared memory but shouldnt it only remove it if Process 2 is not attached to it? I am attaching to the shared memory in process 2 using the following,
boost::interprocess::managed_shared_memory *vfsegment;
vfsegment = new boost::interprocess::managed_shared_memory(boost::interprocess::open_only, "VMySharedMemory");
I am noticing that the shared memory is removed regardless of Process 2 being connected.
I don't believe that there is any mention in the documentation that shared_memory_object::remove
will fail if a process is attached.
Please see this section for reference: Removing shared memory. Particularly:
This function can fail if the shared memory objects does not exist or it's opened by another process.
This means that a call to shared_memory_object::remove("foo")
will attempt to remove shared memory named "foo" no matter what.
The implementation of that function (source here) reflects that behavior:
inline bool shared_memory_object::remove(const char *filename)
{
try{
//Make sure a temporary path is created for shared memory
std::string shmfile;
ipcdetail::tmp_filename(filename, shmfile);
return ipcdetail::delete_file(shmfile.c_str());
}
catch(...){
return false;
}
}
In my experience with released production code, I've had success not calling shared_memory_object::remove
until I no longer need access to the shared memory.
I wrote a very simple example main program that you might find helpful. It will attach to, create, or remove shared memory depending on how you run it. After compiling, try the following steps:
- Run with c to create the shared memory (1.0K by default) and insert dummy data
- Run with o to open ("attach to") the shared memory and read dummy data (reading will happen in a loop every 10 seconds by default)
- In a separate session, run with r to remove the shared memory
- Run again with o to try to open. Notice that this will (almost certainly) fail because the shared memory was (again, almost certainly) removed during the previous step
- Feel free to kill the process from the second step
As to why step 2 above continues to be able to access the data after a call to shared_memory_object::remove
, please see Constructing Managed Shared Memory. Specifically:
When we open a managed shared memory
- A shared memory object is opened.
- The whole shared memory object is mapped in the process' address space.
Mostly likely, because the shared memory object is mapped into the process' address space, the shared memory file itself is no longer directly needed.
I realize that this is a rather contrived example, but I thought something more concrete might be helpful.
#include <cctype> // tolower()
#include <iostream>
#include <string>
#include <unistd.h> // sleep()
#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
int main(int argc, char *argv[])
{
using std::cerr; using std::cout; using std::endl;
using namespace boost::interprocess;
if (argc == 1) {
cout << "usage: " << argv[0] << " <command>\n 'c' create\n 'r' remove\n 'a' attach" << endl;
return 0;
}
const char * shm_name = "shared_memory_segment";
const char * data_name = "the_answer_to_everything";
switch (tolower(argv[1][0])) {
case 'c':
if (shared_memory_object::remove(shm_name)) { cout << "removed: " << shm_name << endl; }
managed_shared_memory(create_only, shm_name, 1024).construct<int>(data_name)(42);
cout << "created: " << shm_name << "\nadded int \"" << data_name << "\": " << 42 << endl;
break;
case 'r':
cout << (shared_memory_object::remove(shm_name) ? "removed: " : "failed to remove: " ) << shm_name << endl;
break;
case 'a':
{
managed_shared_memory segment(open_only, shm_name);
while (true) {
std::pair<int *, std::size_t> data = segment.find<int>( data_name );
if (!data.first || data.second == 0) {
cerr << "Allocation " << data_name << " either not found or empty" << endl;
break;
}
cout << "opened: " << shm_name << " (" << segment.get_segment_manager()->get_size()
<< " bytes)\nretrieved int \"" << data_name << "\": " << *data.first << endl;
sleep(10);
}
}
break;
default:
cerr << "unknown command" << endl;
break;
}
return 0;
}
One additional interesting thing - add one more case:
case 'w':
{
managed_shared_memory segment(open_only, shm_name);
std::pair<int *, std::size_t> data = segment.find<int>( data_name );
if (!data.first || data.second == 0) {
cerr << "Allocation " << data_name << " either not found or empty" << endl;
break;
}
*data.first = 17;
cout << "opened: " << shm_name << " (" << segment.get_segment_manager()->get_size()
<< " bytes)\nretrieved int \"" << data_name << "\": " << *data.first << endl;
}
break;
The aditional option 'w' causes that the memory be attached and written '17' instead ("the most random random number"). With this you can do the following:
Console 1: Do 'c', then 'a'. Reports the memory created with value 42.
Console 2: Do 'w'. On Console1 you'll see that the number is changed.
Console 2: Do 'r'. The memory is successfully removed, Console 1 still prints 17.
Console 2: Do 'c'. It will report memory as created with value 42.
Console 2: Do 'a'. You'll see 42, Console 1 still prints 17.
This confirms - as long as it works the same way on all platforms, but boost declares that it does - that you can use this way to send memory blocks from one process to another, while the "producer" only needs confirmation that the "consumer" attached the block so that "producer" can now remove it. The consumer also doesn't have to detach previous block before attaching the next one.