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