Boost logo

Boost :

Subject: Re: [boost] Could the custom allocator for flyweight hashed_factory be the boost::interprocess::allocator?
From: JOAQUIN M. LOPEZ MUÑOZ (joaquin_at_[hidden])
Date: 2013-06-25 19:23:09


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.

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




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