• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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