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