Boost logo

Boost :

Subject: Re: [boost] Could the custom allocator for flyweight hashed_factory be the boost::interprocess::allocator?
From: Michael Marcin (mike.marcin_at_[hidden])
Date: 2013-06-27 20:46:04


On 6/25/2013 6:23 PM, JOAQUIN M. LOPEZ MUÑOZ wrote:
> H Jin,
> ________________________________________
> De: Boost [boost-bounces_at_[hidden]] en nombre de Jin Chen [chenjin92_at_[hidden]]
> Enviado el: lunes, 24 de junio de 2013 19:56
> Para: Boost_at_[hidden]
> Asunto: [boost] Could the custom allocator for flyweight hashed_factory be the boost::interprocess::allocator?
>
>> I want to use boost::flyweight to do the dedupilcation of
>> boost::interprocess::basic_string object. I wrote a small test program for
>> that. In my program:
>> The flyweight works fine with the interprocess::basic_string which uses the
>> interprocess::allocator only with the default hashed_factory. The default
>> hashed_factory is ideal except that it uses std::allocator instead of
>> interprocess::allocator. I followed the
>> http://www.boost.org/doc/libs/1_53_0/libs/flyweight/doc/tutorial/configuration.html#hashed_factory
>> specifier
>> of a custom hash_factory which can change its allocator to the interprocess
>> one, but then got insanely long compiler error that is super hard to
>> decode...
>
> This is a very interesting exercise! There are several problems with your solution:
>
> * First of all, hashed_factory, as Jonathan points out, assumes the allocator is
> default constructible. As boost::interprocess::allocator needs a segment manager at
> construction time, you can overcome this by writing a custom interprocess allocator
> that takes that info statically, i.e., from a type that returns the segment manager:
>
> struct memory_dat_segment_manager
> {
> typedef ipc::managed_mapped_file::segment_manager type;
>
> static type* get()
> {
> static ipc::managed_mapped_file* segment=
> new ipc::managed_mapped_file(ipc::open_or_create,"memory.dat",409600);
> return segment->get_segment_manager();
> }
> };
>
> template<typename T,typename SegmentManager>
> struct segment_allocator:
> boost::interprocess::allocator<T,typename SegmentManager::type>
> {
> typedef boost::interprocess::allocator<T,typename SegmentManager::type> super;
>
> segment_allocator():super(SegmentManager::get()){}
> template<typename T2>
> segment_allocator(const segment_allocator<T2,SegmentManager>& x):super(x){}
>
> super& base(){return *this;}
>
> template<typename T2>
> struct rebind{
> typedef segment_allocator<T2,SegmentManager> other;
> };
>
> friend void swap(segment_allocator& x,segment_allocator& y)
> {
> swap(x.base(),y.base());
> }
> };
>
> Note that segment_allocator is default constructible: the segment is created on the
> first call to memory_dat_segment_manager.
>
> * Now, the holder class (www.boost.org/libs/flyweight/doc/tutorial/configuration.html#holders )
> needs also to place its content in shared memory. As none of the holders provided by
> Boost.Flyweight does this, we must define our own:
>
> template<typename SegmentManager,typename C>
> struct segment_holder_class:boost::flyweights::holder_marker
> {
> static C& get()
> {
> static C* pc=SegmentManager::get()->
> find_or_construct<C>(typeid(segment_holder_class).name())();
> return *pc;
> }
> };
>
> template<typename SegmentManager>
> struct segment_holder:boost::flyweights::holder_marker
> {
> template<typename C>
> struct apply
> {
> typedef segment_holder_class<SegmentManager,C> type;
> };
> };
>
> Again, the holder uses a SegmentManager class to get access to the segment manager
>
> * Finally, you need an interprocess locking policy
> (www.boost.org/libs/flyweight/doc/tutorial/configuration.html#locking ), and again this needs to
> be custom-defined (note that, in order for labeled_recursive_mutex to be default
> constructible, we get the info needed at construction time (the name of the mutex) from a Name
> class much in the same way we did with SegmentManager before for the allocator and
> holder):
>
> template<typename Name>
> struct labeled_recursive_mutex
> {
> typedef boost::interprocess::named_recursive_mutex mutex_type;
>
> operator mutex_type&()
> {
> static mutex_type mutex(
> boost::interprocess::open_or_create,Name::get());
> return mutex;
> }
> };
>
> template<typename Name>
> struct labeled_locking:boost::flyweights::locking_marker
> {
> typedef labeled_recursive_mutex<Name> mutex_type;
> typedef boost::interprocess::scoped_lock<
> typename labeled_recursive_mutex<Name>::mutex_type> lock_type;
> };
>
> There's a technical issue here (if you don't get the following explanation you might
> as well skip it): As the mutex used by Boost.Flyweight is created by the holder
> class and we are using a shared-memory custom holder, it turns out labeled_recursive_mutex
> is thus created in shared memory. But boost::interprocess::named_recursive_mutex
> cannot be created in shared memory: we solve the problem with operator mutex_type&(),
> which actually creates the real mutex as a static variable in regular memory.
>
> And we're done. Find attached a small complete program showing how to put all the
> pieces together.
>

I just wanted to say I found this response incredibly good.


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk