Reputation: 81
I'm trying to use the boost::interprocess in order to share data between processes and utilize the shared_ptr for lifecycle management. I have a map residing in the shared memory and two processes should access it.
boost::shared_ptr<boost::interprocess::managed_mapped_file> segment =
boost::make_shared<boost::interprocess::managed_mapped_file>
(boost::interprocess::open_or_create,
"./some-mmap.txt", //file name
65536); //segment size in bytes
pair_allocator_type alloc_inst(segment->get_segment_manager());
elements = boost::interprocess::make_managed_shared_ptr(
segment->find_or_construct<map_type>("elements")
(std::less<IdType>(), alloc_inst),
*segment
);
In a test program I have, a Parent and a Child process which essentially both use the piece of code from above. Therefore, they use the same underlying file, same name of the shared object ("elements"), same types, etc.
However, I noticed that whenever a child process dies, the size of the collection dropped to 0. Strange. I investigated and it seemed that it had to do with the destruction of elements
(when this shared pointer goes out of scope). Whenever elements
went out of scope, the size of the underlying collection went to 0.
I also saw that the elements
has the use_count
exactly 1 in both Parent and Child process. For Parent that makes sense, but I don't get it why is it the case for Child. My assumption is that when the Child process dies, the use_count drops to 0, and then the collection is cleared.
What I want is that the pointed object (map) is not destroyed when the Child process dies. I should not make assumptions which processes are active and which ones not.
boost::interprocess::shared_ptr
in a wrong way? EDIT - clarifications on collection
The elements
is a boost::interprocess::map
that maps a certain IdType
to a shared-memory shared pointer to ShmemType
. The size of elements
drops to 0 when the Child process dies.
typedef boost::interprocess::managed_mapped_file::segment_manager segment_manager_type;
typedef std::pair<const IdType, ShmemType::pointer_type> pair_type;
typedef boost::interprocess::allocator<pair_type, segment_manager_type> pair_allocator_type;
typedef boost::interprocess::map<IdType, ShmemType::pointer_type, std::less<IdType>, pair_allocator_type> map_type;
EDIT - example from boost docs
I've taken the examples from boost docs and expanded on it to track down the root cause of my original problem.
#include <boost/interprocess/managed_mapped_file.hpp>
#include <boost/interprocess/containers/vector.hpp>
#include <boost/interprocess/smart_ptr/shared_ptr.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <string>
#include <cstdlib> //std::system
using namespace boost::interprocess;
typedef allocator<int, managed_mapped_file::segment_manager> ShmemAllocator;
typedef vector<int, ShmemAllocator> MyVector;
#include <iostream>
//Main function. For parent process argc == 1, for child process argc == 2
int main(int argc, char *argv[])
{
if(argc == 1){ //Parent process
//Create a new segment with given name and size
managed_mapped_file segment(open_or_create, "./a_MySharedMemory.txt", 65536);
//Initialize shared memory STL-compatible allocator
const ShmemAllocator alloc_inst (segment.get_segment_manager());
// MyVector* elements = segment.find_or_construct<MyVector>("some-vector") //object name
// (alloc_inst);
typedef boost::interprocess::managed_shared_ptr<MyVector, boost::interprocess::managed_mapped_file>::type map_pointer_type;
map_pointer_type elements = boost::interprocess::make_managed_shared_ptr(
segment.find_or_construct<MyVector>("some-vector") //object name
(alloc_inst),
segment
);
for(int i = 0; i < 100; ++i) //Insert data in the vector
elements->push_back(i);
std::cout << elements->size() << std::endl;
std::cout << elements->at(0) << std::endl;
std::cout << elements->at(30) << std::endl;
//Launch child process
std::string s(argv[0]); s += " child ";
if(0 != std::system(s.c_str()))
return 1;
std::cout << elements->size() << std::endl;
std::cout << elements->at(0) << std::endl;
std::cout << elements->at(30) << std::endl;
}
else{ //Child process
//Open the managed segment
managed_mapped_file segment(open_only, "./a_MySharedMemory.txt");
const ShmemAllocator alloc_inst (segment.get_segment_manager());
typedef boost::interprocess::managed_shared_ptr<MyVector, boost::interprocess::managed_mapped_file>::type map_pointer_type;
map_pointer_type elements = boost::interprocess::make_managed_shared_ptr(
segment.find_or_construct<MyVector>("some-vector") //object name
(alloc_inst),
segment
);
// MyVector* elements = segment.find_or_construct<MyVector>("some-vector") //object name
// (alloc_inst);
//Use vector in reverse order
std::sort(elements->rbegin(), elements->rend());
}
return 0;
}
In this case the vector has size == 0 in the parent process after the child process dies. If I use the raw pointer (MyVector* elements = segment.find_or_construct...
), then the collection can be used as expected in the parent process.
So I still have my doubts about the behavior of the shared pointer
Upvotes: 3
Views: 2437
Reputation: 81
I managed to solve the problem. The issue was related to how the shared pointers are made
If you call boost::interprocess::make_managed_shared_ptr
N times to created a shared pointer to an object in shared memory, you get essentially different (completely unrelated) shared pointers to the same object in the shared memory. I was doing that in the Parent and the Child process, and then when the Child process died, the use count went to 0 and erased the pointed object (the map).
The solution was to create a named shared pointer explicitly.
typedef boost::interprocess::allocator<void, segment_manager_type> void_allocator_type;
typedef boost::interprocess::deleter<map_type, segment_manager_type> map_deleter_type;
typedef boost::interprocess::shared_ptr<map_type, void_allocator_type, map_deleter_type> map_pointer_type;
segment_manager_type* segment_manager = segment->get_segment_manager();
pair_allocator_type alloc_inst(segment_manager);
segment->construct<map_pointer_type>("elements ptr")(
segment->construct<map_type>("elements")(std::less<IdClass>(), alloc_inst), //object to own
void_allocator_type(segment_manager), //allocator
map_deleter_type(segment_manager) //deleter
);
And then in the Child process to only get a copy of this pointer
map_pointer_type elements = *segment->find<map_pointer_type>("elements ptr").first;
Upvotes: 2
Reputation: 81
I see now that if I try to create any object in the Parent process in the shared memory after the Child process is dead, I get an assert error:
assertion "hdr->m_value_alignment == algn" failed:
file "/usr/include/boost/interprocess/detail/segment_manager_helper.hpp", line 181,
function: static boost::interprocess::detail::block_header* boost::interprocess::detail::block_header::block_header_from_value(const void*, size_t, size_t)
So now I suspect that it's the way I'm spawning the child process from the parent process.
//Launch child process
std::string s(argv[0]); s += " child";
std::cout << "Child about to be born" << std::endl;
if(0 != std::system(s.c_str())){
std::cout << "(Parent): Child failed!" << std::endl;
return 1;
}
Upvotes: 0
Reputation: 392853
boost::interprocess::shared_ptr
in a wrong way?You're doing it right. You don't need the segment in a shared pointer though. Just make sure the segment outlives any interprocess shared_ptrs (or, for that matter any references into the shared memory segment).
No.
shared_ptr
whose use_count
is shared across processes?Docs: make_managed_shared_ptr
:
Returns an instance of a shared pointer constructed with the default allocator and deleter from a pointer of type T that has been allocated in the passed managed segment.
The other page explicitly mentions (emphasis mine):
Since the reference count and other auxiliary data needed by shared_ptr must be created also in the managed segment, and the deleter has to delete the object from the segment, the user must specify an allocator object and a deleter object when constructing a non-empty instance of shared_ptr
Upvotes: 3