Reputation: 19345
The following program allocates the memory for c
, an object of type C
, within the space of a memory-mapped file. Adding a single character to the vector contained within c
changes the vector's reported size from 0 to 18446744073709551520.
#include <iostream>
#include <new>
#include <boost/interprocess/managed_mapped_file.hpp>
#include <boost/interprocess/offset_ptr.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/filesystem.hpp>
#include <boost/filesystem/operations.hpp>
using namespace boost::interprocess;
class C;
typedef managed_mapped_file::segment_manager SegmentManager;
typedef allocator<void, SegmentManager> VoidAllocator;
typedef allocator<char, SegmentManager> CharAllocator;
typedef allocator<C, SegmentManager> CAllocator;
typedef offset_ptr<C> CPtr;
class C {
public:
std::vector<char, CharAllocator> data;
C(const VoidAllocator &voidAlloc) : data(voidAlloc) {}
void add_char() {
std::cout << data.size() << std::endl;
data.push_back('x');
std::cout << data.size() << std::endl;
}
};
int main(int argc, char *argv[]) {
boost::filesystem::remove_all("file");
managed_mapped_file segment(create_only, "file", 100000);
VoidAllocator allocator_instance(segment.get_segment_manager());
CAllocator alloc_c(allocator_instance);
CPtr c = alloc_c.allocate_one();
*c = C(allocator_instance);
c->add_char();
return 0;
}
The problem does not occur when c
is allocated on the stack, rather than dynamically.
C c(allocator_instance);
c.add_char();
I compile the code on Debian GNU/Linux stretch with Boost 1.62 and g++ 6.3.0-18 with the following command.
g++ -Wall -pthread -lboost_system -lboost_filesystem t.cpp -o t
Upvotes: 1
Views: 127
Reputation: 392954
The allocator returns raw, uninitialized memory.
Indirecting through it as though it pointed to an object of type C
is Undefined Behaviour.
You could - of course - actually do the grunt work using placement-new:
CPtr c = alloc_c.allocate_one();
new (&*c) C(allocator_instance);
Note that, likewise, for non-POD (or actually, non-trivially-destructable types) you will have to remember to also call the destructor at the appropriate time(s):
CPtr c = alloc_c.allocate_one();
new (&*c) C(allocator_instance);
*c = C(allocator_instance);
c->add_char();
c->~C();
alloc_c.deallocate_one(c);
But as you already pointed out the highlevel way avoids new/delete and uses the segment-manager:
CPtr c = segment.construct<C>("Name") (allocator_instance);
Using
find_or_construct
so shared objects can be retrieved by name (use anonymous
or unique
instances if you don't want that; note also that you can instantiate multiple instances through the same names)
using allocator_type
facilitates using scoped allocator adaptors (think: vector<C>
)
parameterized C
makes it easy to use it with standard allocators and shared memory allocators all the same
using the implicit conversion of segment manager pointers and allocator instances
hiding the implementation detail offset_ptr
(which 90% of the time you do not need to know about)
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/managed_mapped_file.hpp>
#include <boost/interprocess/containers/vector.hpp>
#include <iostream>
namespace bip = boost::interprocess;
namespace Shared {
using Segment = bip::managed_mapped_file;
using Manager = Segment::segment_manager;
template <typename T = void>
using Alloc = bip::allocator<T, Manager>;
template <typename T>
using Vector = bip::vector<T, Alloc<T> >;
template <typename Alloc = std::allocator<void> >
struct C {
using allocator_type = Alloc;
bip::vector<char, typename Alloc::template rebind<char>::other> data;
C(Alloc alloc = {}) : data(alloc) {}
void add_char() {
std::cout << data.size() << std::endl;
data.push_back('x');
std::cout << data.size() << std::endl;
}
};
}
int main() {
std::remove("file");
Shared::Segment mmf(bip::create_only, "file", 1000000);
using Alloc = Shared::Alloc<>;
using C = Shared::C<Alloc>;
auto* c = mmf.find_or_construct<C>("byname")(mmf.get_segment_manager());
c->add_char();
//mmf.destroy_ptr(c);
}
Prints
0
1
Upvotes: 1
Reputation: 19345
It seems that moving the constructed object from the default memory pool to the segment one somehow confuses the vector's implementation. Constructing the object directly within the segment memory pool by using the construct
method solves the problem.
CPtr c = segment.construct<C>(anonymous_instance) (allocator_instance);
Upvotes: 1