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