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