1 //////////////////////////////////////////////////////////////////////////////
2 //
3 // (C) Copyright Ion Gaztanaga 2015-2015. 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
11 #define BOOST_CONTAINER_SOURCE
12 #include <boost/container/detail/config_begin.hpp>
13 #include <boost/container/detail/workaround.hpp>
14
15 #include <boost/container/pmr/global_resource.hpp>
16
17 #include <boost/container/detail/pool_resource.hpp>
18 #include <boost/container/detail/block_slist.hpp>
19 #include <boost/container/detail/min_max.hpp>
20 #include <boost/container/detail/placement_new.hpp>
21 #include <boost/intrusive/linear_slist_algorithms.hpp>
22 #include <boost/intrusive/detail/math.hpp>
23
24 #include <cstddef>
25
26 namespace boost {
27 namespace container {
28 namespace pmr {
29
30 //pool_data_t
31
32 class pool_data_t
33 : public block_slist_base<>
34 {
35 typedef block_slist_base<> block_slist_base_t;
36
37 public:
pool_data_t(std::size_t initial_blocks_per_chunk)38 explicit pool_data_t(std::size_t initial_blocks_per_chunk)
39 : block_slist_base_t(), next_blocks_per_chunk(initial_blocks_per_chunk)
40 { slist_algo::init_header(&free_slist); }
41
allocate_block()42 void *allocate_block() BOOST_NOEXCEPT
43 {
44 if(slist_algo::unique(&free_slist)){
45 return 0;
46 }
47 slist_node *pv = slist_algo::node_traits::get_next(&free_slist);
48 slist_algo::unlink_after(&free_slist);
49 pv->~slist_node();
50 return pv;
51 }
52
deallocate_block(void * p)53 void deallocate_block(void *p) BOOST_NOEXCEPT
54 {
55 slist_node *pv = ::new(p, boost_container_new_t()) slist_node();
56 slist_algo::link_after(&free_slist, pv);
57 }
58
release(memory_resource & upstream)59 void release(memory_resource &upstream)
60 {
61 slist_algo::init_header(&free_slist);
62 this->block_slist_base_t::release(upstream);
63 next_blocks_per_chunk = pool_options_minimum_max_blocks_per_chunk;
64 }
65
replenish(memory_resource & mr,std::size_t pool_block,std::size_t max_blocks_per_chunk)66 void replenish(memory_resource &mr, std::size_t pool_block, std::size_t max_blocks_per_chunk)
67 {
68 //Limit max value
69 std::size_t blocks_per_chunk = boost::container::dtl::min_value(max_blocks_per_chunk, next_blocks_per_chunk);
70 //Avoid overflow
71 blocks_per_chunk = boost::container::dtl::min_value(blocks_per_chunk, std::size_t(-1)/pool_block);
72
73 //Minimum block size is at least max_align, so all pools allocate sizes that are multiple of max_align,
74 //meaning that all blocks are max_align-aligned.
75 char *p = static_cast<char *>(block_slist_base_t::allocate(blocks_per_chunk*pool_block, mr));
76
77 //Create header types. This is no-throw
78 for(std::size_t i = 0, max = blocks_per_chunk; i != max; ++i){
79 slist_node *const pv = ::new(p, boost_container_new_t()) slist_node();
80 slist_algo::link_after(&free_slist, pv);
81 p += pool_block;
82 }
83
84 //Update next block per chunk
85 next_blocks_per_chunk = max_blocks_per_chunk/2u < blocks_per_chunk ? max_blocks_per_chunk : blocks_per_chunk*2u;
86 }
87
cache_count() const88 std::size_t cache_count() const
89 { return slist_algo::count(&free_slist) - 1u; }
90
91 slist_node free_slist;
92 std::size_t next_blocks_per_chunk;
93 };
94
95 //pool_resource
96
97 //Detect overflow in ceil_pow2
98 BOOST_STATIC_ASSERT(pool_options_default_max_blocks_per_chunk <= (std::size_t(-1)/2u+1u));
99 //Sanity checks
100 BOOST_STATIC_ASSERT(bi::detail::static_is_pow2<pool_options_default_max_blocks_per_chunk>::value);
101 BOOST_STATIC_ASSERT(bi::detail::static_is_pow2<pool_options_minimum_largest_required_pool_block>::value);
102
103 //unsynchronized_pool_resource
104
priv_limit_option(std::size_t & val,std::size_t min,std::size_t max)105 void pool_resource::priv_limit_option(std::size_t &val, std::size_t min, std::size_t max) //static
106 {
107 if(!val){
108 val = max;
109 }
110 else{
111 val = val < min ? min : boost::container::dtl::min_value(val, max);
112 }
113 }
114
priv_pool_index(std::size_t block_size)115 std::size_t pool_resource::priv_pool_index(std::size_t block_size) //static
116 {
117 //For allocations equal or less than pool_options_minimum_largest_required_pool_block
118 //the smallest pool is used
119 block_size = boost::container::dtl::max_value(block_size, pool_options_minimum_largest_required_pool_block);
120 return bi::detail::ceil_log2(block_size)
121 - bi::detail::ceil_log2(pool_options_minimum_largest_required_pool_block);
122 }
123
priv_pool_block(std::size_t index)124 std::size_t pool_resource::priv_pool_block(std::size_t index) //static
125 {
126 //For allocations equal or less than pool_options_minimum_largest_required_pool_block
127 //the smallest pool is used
128 return pool_options_minimum_largest_required_pool_block << index;
129 }
130
priv_fix_options()131 void pool_resource::priv_fix_options()
132 {
133 priv_limit_option(m_options.max_blocks_per_chunk
134 , pool_options_minimum_max_blocks_per_chunk
135 , pool_options_default_max_blocks_per_chunk);
136 priv_limit_option
137 ( m_options.largest_required_pool_block
138 , pool_options_minimum_largest_required_pool_block
139 , pool_options_default_largest_required_pool_block);
140 m_options.largest_required_pool_block = bi::detail::ceil_pow2(m_options.largest_required_pool_block);
141 }
142
priv_init_pools()143 void pool_resource::priv_init_pools()
144 {
145 const std::size_t num_pools = priv_pool_index(m_options.largest_required_pool_block)+1u;
146 //Otherwise, just use the default alloc (zero pools)
147 void *p = 0;
148 //This can throw
149 p = m_upstream.allocate(sizeof(pool_data_t)*num_pools);
150 //This is nothrow
151 m_pool_data = static_cast<pool_data_t *>(p);
152 for(std::size_t i = 0, max = num_pools; i != max; ++i){
153 ::new(&m_pool_data[i], boost_container_new_t()) pool_data_t(pool_options_minimum_max_blocks_per_chunk);
154 }
155 m_pool_count = num_pools;
156 }
157
priv_constructor_body()158 void pool_resource::priv_constructor_body()
159 {
160 this->priv_fix_options();
161 }
162
pool_resource(const pool_options & opts,memory_resource * upstream)163 pool_resource::pool_resource(const pool_options& opts, memory_resource* upstream) BOOST_NOEXCEPT
164 : m_options(opts), m_upstream(*upstream), m_oversized_list(), m_pool_data(), m_pool_count()
165 { this->priv_constructor_body(); }
166
pool_resource()167 pool_resource::pool_resource() BOOST_NOEXCEPT
168 : m_options(), m_upstream(*get_default_resource()), m_oversized_list(), m_pool_data(), m_pool_count()
169 { this->priv_constructor_body(); }
170
pool_resource(memory_resource * upstream)171 pool_resource::pool_resource(memory_resource* upstream) BOOST_NOEXCEPT
172 : m_options(), m_upstream(*upstream), m_oversized_list(), m_pool_data(), m_pool_count()
173 { this->priv_constructor_body(); }
174
pool_resource(const pool_options & opts)175 pool_resource::pool_resource(const pool_options& opts) BOOST_NOEXCEPT
176 : m_options(opts), m_upstream(*get_default_resource()), m_oversized_list(), m_pool_data(), m_pool_count()
177 { this->priv_constructor_body(); }
178
~pool_resource()179 pool_resource::~pool_resource() //virtual
180 {
181 this->release();
182
183 for(std::size_t i = 0, max = m_pool_count; i != max; ++i){
184 m_pool_data[i].~pool_data_t();
185 }
186 if(m_pool_data){
187 m_upstream.deallocate((void*)m_pool_data, sizeof(pool_data_t)*m_pool_count);
188 }
189 }
190
release()191 void pool_resource::release()
192 {
193 m_oversized_list.release(m_upstream);
194 for(std::size_t i = 0, max = m_pool_count; i != max; ++i)
195 {
196 m_pool_data[i].release(m_upstream);
197 }
198 }
199
upstream_resource() const200 memory_resource* pool_resource::upstream_resource() const
201 { return &m_upstream; }
202
options() const203 pool_options pool_resource::options() const
204 { return m_options; }
205
do_allocate(std::size_t bytes,std::size_t alignment)206 void* pool_resource::do_allocate(std::size_t bytes, std::size_t alignment) //virtual
207 {
208 if(!m_pool_data){
209 this->priv_init_pools();
210 }
211 (void)alignment; //alignment ignored here, max_align is used by pools
212 if(bytes > m_options.largest_required_pool_block){
213 return m_oversized_list.allocate(bytes, m_upstream);
214 }
215 else{
216 const std::size_t pool_idx = priv_pool_index(bytes);
217 pool_data_t & pool = m_pool_data[pool_idx];
218 void *p = pool.allocate_block();
219 if(!p){
220 pool.replenish(m_upstream, priv_pool_block(pool_idx), m_options.max_blocks_per_chunk);
221 p = pool.allocate_block();
222 }
223 return p;
224 }
225 }
226
do_deallocate(void * p,std::size_t bytes,std::size_t alignment)227 void pool_resource::do_deallocate(void* p, std::size_t bytes, std::size_t alignment) //virtual
228 {
229 (void)alignment; //alignment ignored here, max_align is used by pools
230 if(bytes > m_options.largest_required_pool_block){
231 //Just cached
232 return m_oversized_list.deallocate(p, m_upstream);
233 }
234 else{
235 const std::size_t pool_idx = priv_pool_index(bytes);
236 return m_pool_data[pool_idx].deallocate_block(p);
237 }
238 }
239
do_is_equal(const memory_resource & other) const240 bool pool_resource::do_is_equal(const memory_resource& other) const BOOST_NOEXCEPT //virtual
241 { return this == dynamic_cast<const pool_resource*>(&other); }
242
243
pool_count() const244 std::size_t pool_resource::pool_count() const
245 {
246 if(BOOST_LIKELY((0 != m_pool_data))){
247 return m_pool_count;
248 }
249 else{
250 return priv_pool_index(m_options.largest_required_pool_block)+1u;
251 }
252 }
253
pool_index(std::size_t bytes) const254 std::size_t pool_resource::pool_index(std::size_t bytes) const
255 {
256 if(bytes > m_options.largest_required_pool_block){
257 return pool_count();
258 }
259 else{
260 return priv_pool_index(bytes);
261 }
262 }
263
pool_next_blocks_per_chunk(std::size_t pool_idx) const264 std::size_t pool_resource::pool_next_blocks_per_chunk(std::size_t pool_idx) const
265 {
266 if(BOOST_LIKELY((m_pool_data && pool_idx < m_pool_count))){
267 return m_pool_data[pool_idx].next_blocks_per_chunk;
268 }
269 else{
270 return 1u;
271 }
272 }
273
pool_block(std::size_t pool_idx) const274 std::size_t pool_resource::pool_block(std::size_t pool_idx) const
275 { return priv_pool_block(pool_idx); }
276
pool_cached_blocks(std::size_t pool_idx) const277 std::size_t pool_resource::pool_cached_blocks(std::size_t pool_idx) const
278 {
279 if(BOOST_LIKELY((m_pool_data && pool_idx < m_pool_count))){
280 return m_pool_data[pool_idx].cache_count();
281 }
282 else{
283 return 0u;
284 }
285 }
286
287 } //namespace pmr {
288 } //namespace container {
289 } //namespace boost {
290
291 #include <boost/container/detail/config_end.hpp>
292