1 /*****************************************************************************
2 
3 Copyright (c) 2013, 2019, Oracle and/or its affiliates. All Rights Reserved.
4 
5 This program is free software; you can redistribute it and/or modify it under
6 the terms of the GNU General Public License, version 2.0, as published by the
7 Free Software Foundation.
8 
9 This program is also distributed with certain software (including but not
10 limited to OpenSSL) that is licensed under separate terms, as designated in a
11 particular file or component or in included license documentation. The authors
12 of MySQL hereby grant you an additional permission to link the program and
13 your derivative works with the separately licensed software that they have
14 included with MySQL.
15 
16 This program is distributed in the hope that it will be useful, but WITHOUT
17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18 FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
19 for more details.
20 
21 You should have received a copy of the GNU General Public License along with
22 this program; if not, write to the Free Software Foundation, Inc.,
23 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
24 
25 *****************************************************************************/
26 
27 /** @file include/ut0pool.h
28  Object pool.
29 
30  Created 2012-Feb-26 Sunny Bains
31  ***********************************************************************/
32 
33 #ifndef ut0pool_h
34 #define ut0pool_h
35 
36 #include <functional>
37 #include <queue>
38 #include <vector>
39 
40 #include "ut0new.h"
41 
42 /** Allocate the memory for the object in blocks. We keep the objects sorted
43 on pointer so that they are closer together in case they have to be iterated
44 over in a list. */
45 template <typename Type, typename Factory, typename LockStrategy>
46 struct Pool {
47   typedef Type value_type;
48 
49   // FIXME: Add an assertion to check alignment and offset is
50   // as we expect it. Also, sizeof(void*) can be 8, can we impove on this.
51   struct Element {
52     Pool *m_pool;
53     value_type m_type;
54   };
55 
56   /** Constructor
57   @param size size of the memory block */
PoolPool58   Pool(size_t size) : m_end(), m_start(), m_size(size), m_last() {
59     ut_a(size >= sizeof(Element));
60 
61     m_lock_strategy.create();
62 
63     ut_a(m_start == nullptr);
64 
65     m_start = reinterpret_cast<Element *>(ut_zalloc_nokey(m_size));
66 
67     m_last = m_start;
68 
69     m_end = &m_start[m_size / sizeof(*m_start)];
70 
71     /* Note: Initialise only a small subset, even though we have
72     allocated all the memory. This is required only because PFS
73     (MTR) results change if we instantiate too many mutexes up
74     front. */
75 
76     init(ut_min(size_t(16), size_t(m_end - m_start)));
77 
78     ut_ad(m_pqueue.size() <= size_t(m_last - m_start));
79   }
80 
81   /** Destructor */
~PoolPool82   ~Pool() {
83     m_lock_strategy.destroy();
84 
85     for (Element *elem = m_start; elem != m_last; ++elem) {
86       ut_ad(elem->m_pool == this);
87       Factory::destroy(&elem->m_type);
88     }
89 
90     ut_free(m_start);
91     m_end = m_last = m_start = nullptr;
92     m_size = 0;
93   }
94 
95   /** Get an object from the pool.
96   @return a free instance or NULL if exhausted. */
getPool97   Type *get() {
98     Element *elem;
99 
100     m_lock_strategy.enter();
101 
102     if (!m_pqueue.empty()) {
103       elem = m_pqueue.top();
104       m_pqueue.pop();
105 
106     } else if (m_last < m_end) {
107       /* Initialise the remaining elements. */
108       init(m_end - m_last);
109 
110       ut_ad(!m_pqueue.empty());
111 
112       elem = m_pqueue.top();
113       m_pqueue.pop();
114     } else {
115       elem = nullptr;
116     }
117 
118     m_lock_strategy.exit();
119 
120     return (elem != nullptr ? &elem->m_type : nullptr);
121   }
122 
123   /** Add the object to the pool.
124   @param ptr object to free */
mem_freePool125   static void mem_free(value_type *ptr) {
126     Element *elem;
127     byte *p = reinterpret_cast<byte *>(ptr + 1);
128 
129     elem = reinterpret_cast<Element *>(p - sizeof(*elem));
130 
131     elem->m_pool->put(elem);
132   }
133 
134  protected:
135   // Disable copying
136   Pool(const Pool &);
137   Pool &operator=(const Pool &);
138 
139  private:
140   /* We only need to compare on pointer address. */
141   typedef std::priority_queue<Element *,
142                               std::vector<Element *, ut_allocator<Element *>>,
143                               std::greater<Element *>>
144       pqueue_t;
145 
146   /** Release the object to the free pool
147   @param elem element to free */
putPool148   void put(Element *elem) {
149     m_lock_strategy.enter();
150 
151     ut_ad(elem >= m_start && elem < m_last);
152 
153     ut_ad(Factory::debug(&elem->m_type));
154 
155     m_pqueue.push(elem);
156 
157     m_lock_strategy.exit();
158   }
159 
160   /** Initialise the elements.
161   @param n_elems Number of elements to initialise */
initPool162   void init(size_t n_elems) {
163     ut_ad(size_t(m_end - m_last) >= n_elems);
164 
165     for (size_t i = 0; i < n_elems; ++i, ++m_last) {
166       m_last->m_pool = this;
167       Factory::init(&m_last->m_type);
168       m_pqueue.push(m_last);
169     }
170 
171     ut_ad(m_last <= m_end);
172   }
173 
174  private:
175   /** Pointer to the last element */
176   Element *m_end;
177 
178   /** Pointer to the first element */
179   Element *m_start;
180 
181   /** Size of the block in bytes */
182   size_t m_size;
183 
184   /** Upper limit of used space */
185   Element *m_last;
186 
187   /** Priority queue ordered on the pointer addresse. */
188   pqueue_t m_pqueue;
189 
190   /** Lock strategy to use */
191   LockStrategy m_lock_strategy;
192 };
193 
194 template <typename Pool, typename LockStrategy>
195 struct PoolManager {
196   typedef Pool PoolType;
197   typedef typename PoolType::value_type value_type;
198 
PoolManagerPoolManager199   PoolManager(size_t size) : m_size(size) { create(); }
200 
~PoolManagerPoolManager201   ~PoolManager() {
202     destroy();
203 
204     ut_a(m_pools.empty());
205   }
206 
207   /** Get an element from one of the pools.
208   @return instance or NULL if pool is empty. */
getPoolManager209   value_type *get() {
210     size_t index = 0;
211     size_t delay = 1;
212     value_type *ptr = nullptr;
213 
214     do {
215       m_lock_strategy.enter();
216 
217       ut_ad(!m_pools.empty());
218 
219       size_t n_pools = m_pools.size();
220 
221       PoolType *pool = m_pools[index % n_pools];
222 
223       m_lock_strategy.exit();
224 
225       ptr = pool->get();
226 
227       if (ptr == nullptr && (index / n_pools) > 2) {
228         if (!add_pool(n_pools)) {
229           ib::error(ER_IB_MSG_FAILED_TO_ALLOCATE_WAIT, m_size, delay);
230 
231           /* There is nothing much we can do
232           except crash and burn, however lets
233           be a little optimistic and wait for
234           a resource to be freed. */
235           os_thread_sleep(delay * 1000000);
236 
237           if (delay < 32) {
238             delay <<= 1;
239           }
240 
241         } else {
242           delay = 1;
243         }
244       }
245 
246       ++index;
247 
248     } while (ptr == nullptr);
249 
250     return (ptr);
251   }
252 
mem_freePoolManager253   static void mem_free(value_type *ptr) { PoolType::mem_free(ptr); }
254 
255  private:
256   /** Add a new pool
257   @param n_pools Number of pools that existed when the add pool was
258                   called.
259   @return true on success */
add_poolPoolManager260   bool add_pool(size_t n_pools) {
261     bool added = false;
262 
263     m_lock_strategy.enter();
264 
265     if (n_pools < m_pools.size()) {
266       /* Some other thread already added a pool. */
267       added = true;
268     } else {
269       PoolType *pool;
270 
271       ut_ad(n_pools == m_pools.size());
272 
273       pool = UT_NEW_NOKEY(PoolType(m_size));
274 
275       if (pool != nullptr) {
276         ut_ad(n_pools <= m_pools.size());
277 
278         m_pools.push_back(pool);
279 
280         ib::info(ER_IB_MSG_NUM_POOLS, m_pools.size());
281 
282         added = true;
283       }
284     }
285 
286     ut_ad(n_pools < m_pools.size() || !added);
287 
288     m_lock_strategy.exit();
289 
290     return (added);
291   }
292 
293   /** Create the pool manager. */
createPoolManager294   void create() {
295     ut_a(m_size > sizeof(value_type));
296     m_lock_strategy.create();
297 
298     add_pool(0);
299   }
300 
301   /** Release the resources. */
destroyPoolManager302   void destroy() {
303     typename Pools::iterator it;
304     typename Pools::iterator end = m_pools.end();
305 
306     for (it = m_pools.begin(); it != end; ++it) {
307       PoolType *pool = *it;
308 
309       UT_DELETE(pool);
310     }
311 
312     m_pools.clear();
313 
314     m_lock_strategy.destroy();
315   }
316 
317  private:
318   // Disable copying
319   PoolManager(const PoolManager &);
320   PoolManager &operator=(const PoolManager &);
321 
322   typedef std::vector<PoolType *, ut_allocator<PoolType *>> Pools;
323 
324   /** Size of each block */
325   size_t m_size;
326 
327   /** Pools managed this manager */
328   Pools m_pools;
329 
330   /** Lock strategy to use */
331   LockStrategy m_lock_strategy;
332 };
333 
334 #endif /* ut0pool_h */
335