Re: [boost] Could the custom allocator for flyweight hashed_factory be the boost::interprocess::allocator?

H Jin, ________________________________________ De: Boost [boost-bounces@lists.boost.org] en nombre de Jin Chen [chenjin92@gmail.com] Enviado el: lunes, 24 de junio de 2013 19:56 Para: Boost@lists.boost.org 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/configurati... 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. Best, Joaquín M López Muñoz Telefónica Digital ________________________________ Este mensaje se dirige exclusivamente a su destinatario. Puede consultar nuestra política de envío y recepción de correo electrónico en el enlace situado más abajo. This message is intended exclusively for its addressee. We only send and receive email on the basis of the terms set out at: http://www.tid.es/ES/PAGINAS/disclaimer.aspx

On 6/25/2013 6:23 PM, JOAQUIN M. LOPEZ MUÑOZ wrote:
H Jin, ________________________________________ De: Boost [boost-bounces@lists.boost.org] en nombre de Jin Chen [chenjin92@gmail.com] Enviado el: lunes, 24 de junio de 2013 19:56 Para: Boost@lists.boost.org 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/configurati... 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.
participants (2)
-
JOAQUIN M. LOPEZ MUÑOZ
-
Michael Marcin