1 //////////////////////////////////////////////////////////////////////////////
2 //
3 // (C) Copyright Ion Gaztanaga 2005-2013. Distributed under the Boost
4 // Software License, Version 1.0. (See accompanying file
5 // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // See http://www.boost.org/libs/container for documentation.
8 //
9 //////////////////////////////////////////////////////////////////////////////
10 #ifndef BOOST_CONTAINER_DETAIL_NODE_POOL_IMPL_HPP
11 #define BOOST_CONTAINER_DETAIL_NODE_POOL_IMPL_HPP
12 
13 #ifndef BOOST_CONFIG_HPP
14 #  include <boost/config.hpp>
15 #endif
16 
17 #if defined(BOOST_HAS_PRAGMA_ONCE)
18 #  pragma once
19 #endif
20 
21 #include <boost/container/detail/config_begin.hpp>
22 #include <boost/container/detail/workaround.hpp>
23 #include <boost/container/container_fwd.hpp>
24 
25 #include <boost/container/detail/math_functions.hpp>
26 #include <boost/container/detail/mpl.hpp>
27 #include <boost/container/detail/pool_common.hpp>
28 #include <boost/move/detail/to_raw_pointer.hpp>
29 #include <boost/container/detail/type_traits.hpp>
30 
31 #include <boost/intrusive/pointer_traits.hpp>
32 #include <boost/intrusive/set.hpp>
33 #include <boost/intrusive/slist.hpp>
34 
35 #include <boost/core/no_exceptions_support.hpp>
36 #include <boost/assert.hpp>
37 #include <cstddef>
38 
39 namespace boost {
40 namespace container {
41 namespace dtl {
42 
43 template<class SegmentManagerBase>
44 class private_node_pool_impl
45 {
46    //Non-copyable
47    private_node_pool_impl();
48    private_node_pool_impl(const private_node_pool_impl &);
49    private_node_pool_impl &operator=(const private_node_pool_impl &);
50 
51    //A node object will hold node_t when it's not allocated
52    public:
53    typedef typename SegmentManagerBase::void_pointer              void_pointer;
54    typedef typename node_slist<void_pointer>::slist_hook_t        slist_hook_t;
55    typedef typename node_slist<void_pointer>::node_t              node_t;
56    typedef typename node_slist<void_pointer>::node_slist_t        free_nodes_t;
57    typedef typename SegmentManagerBase::multiallocation_chain     multiallocation_chain;
58    typedef typename SegmentManagerBase::size_type                 size_type;
59 
60    private:
61    typedef typename bi::make_slist
62       < node_t, bi::base_hook<slist_hook_t>
63       , bi::linear<true>
64       , bi::constant_time_size<false> >::type      blockslist_t;
65 
get_rounded_size(size_type orig_size,size_type round_to)66    static size_type get_rounded_size(size_type orig_size, size_type round_to)
67    {  return ((orig_size-1)/round_to+1)*round_to;  }
68 
69    public:
70 
71    //!Segment manager typedef
72    typedef SegmentManagerBase segment_manager_base_type;
73 
74    //!Constructor from a segment manager. Never throws
private_node_pool_impl(segment_manager_base_type * segment_mngr_base,size_type node_size,size_type nodes_per_block)75    private_node_pool_impl(segment_manager_base_type *segment_mngr_base, size_type node_size, size_type nodes_per_block)
76    :  m_nodes_per_block(nodes_per_block)
77    ,  m_real_node_size(lcm(node_size, size_type(alignment_of<node_t>::value)))
78       //General purpose allocator
79    ,  mp_segment_mngr_base(segment_mngr_base)
80    ,  m_blocklist()
81    ,  m_freelist()
82       //Debug node count
83    ,  m_allocated(0)
84    {}
85 
86    //!Destructor. Deallocates all allocated blocks. Never throws
~private_node_pool_impl()87    ~private_node_pool_impl()
88    {  this->purge_blocks();  }
89 
get_real_num_node() const90    size_type get_real_num_node() const
91    {  return m_nodes_per_block; }
92 
93    //!Returns the segment manager. Never throws
get_segment_manager_base() const94    segment_manager_base_type* get_segment_manager_base()const
95    {  return boost::movelib::to_raw_pointer(mp_segment_mngr_base);  }
96 
allocate_node()97    void *allocate_node()
98    {  return this->priv_alloc_node();  }
99 
100    //!Deallocates an array pointed by ptr. Never throws
deallocate_node(void * ptr)101    void deallocate_node(void *ptr)
102    {  this->priv_dealloc_node(ptr); }
103 
104    //!Allocates a singly linked list of n nodes ending in null pointer.
allocate_nodes(const size_type n,multiallocation_chain & chain)105    void allocate_nodes(const size_type n, multiallocation_chain &chain)
106    {
107       //Preallocate all needed blocks to fulfill the request
108       size_type cur_nodes = m_freelist.size();
109       if(cur_nodes < n){
110          this->priv_alloc_block(((n - cur_nodes) - 1)/m_nodes_per_block + 1);
111       }
112 
113       //We just iterate the needed nodes to get the last we'll erase
114       typedef typename free_nodes_t::iterator free_iterator;
115       free_iterator before_last_new_it = m_freelist.before_begin();
116       for(size_type j = 0; j != n; ++j){
117          ++before_last_new_it;
118       }
119 
120       //Cache the first node of the allocated range before erasing
121       free_iterator first_node(m_freelist.begin());
122       free_iterator last_node (before_last_new_it);
123 
124       //Erase the range. Since we already have the distance, this is O(1)
125       m_freelist.erase_after( m_freelist.before_begin()
126                             , ++free_iterator(before_last_new_it)
127                             , n);
128 
129       //Now take the last erased node and just splice it in the end
130       //of the intrusive list that will be traversed by the multialloc iterator.
131       chain.incorporate_after(chain.before_begin(), &*first_node, &*last_node, n);
132       m_allocated += n;
133    }
134 
deallocate_nodes(multiallocation_chain & chain)135    void deallocate_nodes(multiallocation_chain &chain)
136    {
137       typedef typename multiallocation_chain::iterator iterator;
138       iterator it(chain.begin()), itend(chain.end());
139       while(it != itend){
140          void *pElem = &*it;
141          ++it;
142          this->priv_dealloc_node(pElem);
143       }
144    }
145 
146    //!Deallocates all the free blocks of memory. Never throws
deallocate_free_blocks()147    void deallocate_free_blocks()
148    {
149       typedef typename free_nodes_t::iterator nodelist_iterator;
150       typename blockslist_t::iterator bit(m_blocklist.before_begin()),
151                                       it(m_blocklist.begin()),
152                                       itend(m_blocklist.end());
153       free_nodes_t backup_list;
154       nodelist_iterator backup_list_last = backup_list.before_begin();
155 
156       //Execute the algorithm and get an iterator to the last value
157       size_type blocksize = (get_rounded_size)
158          (m_real_node_size*m_nodes_per_block, (size_type) alignment_of<node_t>::value);
159 
160       while(it != itend){
161          //Collect all the nodes from the block pointed by it
162          //and push them in the list
163          free_nodes_t free_nodes;
164          nodelist_iterator last_it = free_nodes.before_begin();
165          const void *addr = get_block_from_hook(&*it, blocksize);
166 
167          m_freelist.remove_and_dispose_if
168             (is_between(addr, blocksize), push_in_list(free_nodes, last_it));
169 
170          //If the number of nodes is equal to m_nodes_per_block
171          //this means that the block can be deallocated
172          if(free_nodes.size() == m_nodes_per_block){
173             //Unlink the nodes
174             free_nodes.clear();
175             it = m_blocklist.erase_after(bit);
176             mp_segment_mngr_base->deallocate((void*)addr);
177          }
178          //Otherwise, insert them in the backup list, since the
179          //next "remove_if" does not need to check them again.
180          else{
181             //Assign the iterator to the last value if necessary
182             if(backup_list.empty() && !m_freelist.empty()){
183                backup_list_last = last_it;
184             }
185             //Transfer nodes. This is constant time.
186             backup_list.splice_after
187                ( backup_list.before_begin()
188                , free_nodes
189                , free_nodes.before_begin()
190                , last_it
191                , free_nodes.size());
192             bit = it;
193             ++it;
194          }
195       }
196       //We should have removed all the nodes from the free list
197       BOOST_ASSERT(m_freelist.empty());
198 
199       //Now pass all the node to the free list again
200       m_freelist.splice_after
201          ( m_freelist.before_begin()
202          , backup_list
203          , backup_list.before_begin()
204          , backup_list_last
205          , backup_list.size());
206    }
207 
num_free_nodes()208    size_type num_free_nodes()
209    {  return m_freelist.size();  }
210 
211    //!Deallocates all used memory. Precondition: all nodes allocated from this pool should
212    //!already be deallocated. Otherwise, undefined behaviour. Never throws
purge_blocks()213    void purge_blocks()
214    {
215       //check for memory leaks
216       BOOST_ASSERT(m_allocated==0);
217       size_type blocksize = (get_rounded_size)
218          (m_real_node_size*m_nodes_per_block, (size_type)alignment_of<node_t>::value);
219 
220       //We iterate though the NodeBlock list to free the memory
221       while(!m_blocklist.empty()){
222          void *addr = get_block_from_hook(&m_blocklist.front(), blocksize);
223          m_blocklist.pop_front();
224          mp_segment_mngr_base->deallocate((void*)addr);
225       }
226       //Just clear free node list
227       m_freelist.clear();
228    }
229 
swap(private_node_pool_impl & other)230    void swap(private_node_pool_impl &other)
231    {
232       BOOST_ASSERT(m_nodes_per_block == other.m_nodes_per_block);
233       BOOST_ASSERT(m_real_node_size == other.m_real_node_size);
234       std::swap(mp_segment_mngr_base, other.mp_segment_mngr_base);
235       m_blocklist.swap(other.m_blocklist);
236       m_freelist.swap(other.m_freelist);
237       std::swap(m_allocated, other.m_allocated);
238    }
239 
240    private:
241 
242    struct push_in_list
243    {
push_in_listboost::container::dtl::private_node_pool_impl::push_in_list244       push_in_list(free_nodes_t &l, typename free_nodes_t::iterator &it)
245          :  slist_(l), last_it_(it)
246       {}
247 
operator ()boost::container::dtl::private_node_pool_impl::push_in_list248       void operator()(typename free_nodes_t::pointer p) const
249       {
250          slist_.push_front(*p);
251          if(slist_.size() == 1){ //Cache last element
252             ++last_it_ = slist_.begin();
253          }
254       }
255 
256       private:
257       free_nodes_t &slist_;
258       typename free_nodes_t::iterator &last_it_;
259    };
260 
261    struct is_between
262    {
263       typedef typename free_nodes_t::value_type argument_type;
264       typedef bool                              result_type;
265 
is_betweenboost::container::dtl::private_node_pool_impl::is_between266       is_between(const void *addr, std::size_t size)
267          :  beg_(static_cast<const char *>(addr)), end_(beg_+size)
268       {}
269 
operator ()boost::container::dtl::private_node_pool_impl::is_between270       bool operator()(typename free_nodes_t::const_reference v) const
271       {
272          return (beg_ <= reinterpret_cast<const char *>(&v) &&
273                  end_ >  reinterpret_cast<const char *>(&v));
274       }
275       private:
276       const char *      beg_;
277       const char *      end_;
278    };
279 
280    //!Allocates one node, using single segregated storage algorithm.
281    //!Never throws
priv_alloc_node()282    node_t *priv_alloc_node()
283    {
284       //If there are no free nodes we allocate a new block
285       if (m_freelist.empty())
286          this->priv_alloc_block(1);
287       //We take the first free node
288       node_t *n = (node_t*)&m_freelist.front();
289       m_freelist.pop_front();
290       ++m_allocated;
291       return n;
292    }
293 
294    //!Deallocates one node, using single segregated storage algorithm.
295    //!Never throws
priv_dealloc_node(void * pElem)296    void priv_dealloc_node(void *pElem)
297    {
298       //We put the node at the beginning of the free node list
299       node_t * to_deallocate = static_cast<node_t*>(pElem);
300       m_freelist.push_front(*to_deallocate);
301       BOOST_ASSERT(m_allocated>0);
302       --m_allocated;
303    }
304 
305    //!Allocates several blocks of nodes. Can throw
priv_alloc_block(size_type num_blocks)306    void priv_alloc_block(size_type num_blocks)
307    {
308       BOOST_ASSERT(num_blocks > 0);
309       size_type blocksize =
310          (get_rounded_size)(m_real_node_size*m_nodes_per_block, (size_type)alignment_of<node_t>::value);
311 
312       BOOST_TRY{
313          for(size_type i = 0; i != num_blocks; ++i){
314             //We allocate a new NodeBlock and put it as first
315             //element in the free Node list
316             char *pNode = reinterpret_cast<char*>
317                (mp_segment_mngr_base->allocate(blocksize + sizeof(node_t)));
318             char *pBlock = pNode;
319             m_blocklist.push_front(get_block_hook(pBlock, blocksize));
320 
321             //We initialize all Nodes in Node Block to insert
322             //them in the free Node list
323             for(size_type j = 0; j < m_nodes_per_block; ++j, pNode += m_real_node_size){
324                m_freelist.push_front(*new (pNode) node_t);
325             }
326          }
327       }
328       BOOST_CATCH(...){
329          //to-do: if possible, an efficient way to deallocate allocated blocks
330          BOOST_RETHROW
331       }
332       BOOST_CATCH_END
333    }
334 
335    //!Deprecated, use deallocate_free_blocks
deallocate_free_chunks()336    void deallocate_free_chunks()
337    {  this->deallocate_free_blocks(); }
338 
339    //!Deprecated, use purge_blocks
purge_chunks()340    void purge_chunks()
341    {  this->purge_blocks(); }
342 
343    private:
344    //!Returns a reference to the block hook placed in the end of the block
get_block_hook(void * block,size_type blocksize)345    static node_t & get_block_hook (void *block, size_type blocksize)
346    {
347       return *reinterpret_cast<node_t*>(reinterpret_cast<char*>(block) + blocksize);
348    }
349 
350    //!Returns the starting address of the block reference to the block hook placed in the end of the block
get_block_from_hook(node_t * hook,size_type blocksize)351    void *get_block_from_hook (node_t *hook, size_type blocksize)
352    {
353       return (reinterpret_cast<char*>(hook) - blocksize);
354    }
355 
356    private:
357    typedef typename boost::intrusive::pointer_traits
358       <void_pointer>::template rebind_pointer<segment_manager_base_type>::type   segment_mngr_base_ptr_t;
359 
360    const size_type m_nodes_per_block;
361    const size_type m_real_node_size;
362    segment_mngr_base_ptr_t mp_segment_mngr_base;   //Segment manager
363    blockslist_t      m_blocklist;      //Intrusive container of blocks
364    free_nodes_t      m_freelist;       //Intrusive container of free nods
365    size_type       m_allocated;      //Used nodes for debugging
366 };
367 
368 
369 }  //namespace dtl {
370 }  //namespace container {
371 }  //namespace boost {
372 
373 #include <boost/container/detail/config_end.hpp>
374 
375 #endif   //#ifndef BOOST_CONTAINER_DETAIL_ADAPTIVE_NODE_POOL_IMPL_HPP
376