1 /**
2  * \file mlt_pool.c
3  * \brief memory pooling functionality
4  * \see mlt_pool_s
5  *
6  * Copyright (C) 2003-2018 Meltytech, LLC
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21  */
22 
23 #include "mlt_properties.h"
24 #include "mlt_deque.h"
25 #include "mlt_log.h"
26 
27 #include <stdlib.h>
28 #include <string.h>
29 #include <pthread.h>
30 
31 // Not nice - memalign is defined here apparently?
32 #ifdef linux
33 #include <malloc.h>
34 #endif
35 
36 // Macros to re-assign system functions.
37 #ifdef _WIN32
38 #  define mlt_free _aligned_free
39 #  define mlt_alloc(X) _aligned_malloc( (X), 16 )
40 #  define mlt_realloc(X, Y) _aligned_realloc( (X), (Y), 16 )
41 #else
42 #  define mlt_free free
43 #  ifdef linux
44 #    define mlt_alloc(X) memalign( 16, (X) )
45 #  else
46 #    define mlt_alloc(X) malloc( (X) )
47 #  endif
48 #  define mlt_realloc realloc
49 #endif
50 
51 // We now require a compile-time define to use mlt_pool.
52 #ifndef USE_MLT_POOL
53 #define USE_MLT_POOL 1
54 #endif
55 
56 #if !USE_MLT_POOL
57 
mlt_pool_init()58 void mlt_pool_init() {}
mlt_pool_alloc(int size)59 void *mlt_pool_alloc( int size ) { return mlt_alloc( size ); }
mlt_pool_realloc(void * ptr,int size)60 void *mlt_pool_realloc( void *ptr, int size ) { return mlt_realloc( ptr, size ); }
mlt_pool_release(void * release)61 void mlt_pool_release( void *release ) { return mlt_free( release ); }
mlt_pool_purge()62 void mlt_pool_purge() {}
mlt_pool_close()63 void mlt_pool_close() {}
mlt_pool_stat()64 void mlt_pool_stat() {}
65 
66 #else
67 
68 /** global singleton for tracking pools */
69 
70 static mlt_properties pools = NULL;
71 
72 /** \brief Pool (memory) class
73  */
74 
75 typedef struct mlt_pool_s
76 {
77 	pthread_mutex_t lock; ///< lock to prevent race conditions
78 	mlt_deque stack;      ///< a stack of addresses to memory blocks
79 	int size;             ///< the size of the memory block as a power of 2
80 	int count;            ///< the number of blocks in the pool
81 }
82 *mlt_pool;
83 
84 /** \brief private to mlt_pool_s, for tracking items to release
85  *
86  * Aligned to 16 byte in case we toss buffers to external assembly
87  * optimized libraries (sse/altivec).
88  */
89 
90 typedef struct __attribute__ ((aligned (16))) mlt_release_s
91 {
92 	mlt_pool pool;
93 	int references;
94 }
95 *mlt_release;
96 
97 /** Create a pool.
98  *
99  * \private \memberof mlt_pool_s
100  * \param size the size of the memory blocks to hold as some power of two
101  * \return a new pool object
102  */
103 
pool_init(int size)104 static mlt_pool pool_init( int size )
105 {
106 	// Create the pool
107 	mlt_pool self = calloc( 1, sizeof( struct mlt_pool_s ) );
108 
109 	// Initialise it
110 	if ( self != NULL )
111 	{
112 		// Initialise the mutex
113 		pthread_mutex_init( &self->lock, NULL );
114 
115 		// Create the stack
116 		self->stack = mlt_deque_init( );
117 
118 		// Assign the size
119 		self->size = size;
120 	}
121 
122 	// Return it
123 	return self;
124 }
125 
126 /** Get an item from the pool.
127  *
128  * \private \memberof mlt_pool_s
129  * \param self a pool
130  * \return an opaque pointer
131  */
132 
pool_fetch(mlt_pool self)133 static void *pool_fetch( mlt_pool self )
134 {
135 	// We will generate a release object
136 	void *ptr = NULL;
137 
138 	// Sanity check
139 	if ( self != NULL )
140 	{
141 		// Lock the pool
142 		pthread_mutex_lock( &self->lock );
143 
144 		// Check if the stack is empty
145 		if ( mlt_deque_count( self->stack ) != 0 )
146 		{
147 			// Pop the top of the stack
148 			ptr = mlt_deque_pop_back( self->stack );
149 
150 			// Assign the reference
151 			( ( mlt_release )ptr )->references = 1;
152 		}
153 		else
154 		{
155 			// We need to generate a release item
156 			mlt_release release = mlt_alloc( self->size );
157 
158 			// If out of memory, log it, reclaim memory, and try again.
159 			if ( !release && self->size > 0 )
160 			{
161 				mlt_log_fatal( NULL, "[mlt_pool] out of memory\n" );
162 				mlt_pool_purge();
163 				release = mlt_alloc( self->size );
164 			}
165 
166 			// Initialise it
167 			if ( release != NULL )
168 			{
169 				// Increment the number of items allocated to this pool
170 				self->count ++;
171 
172 				// Assign the pool
173 				release->pool = self;
174 
175 				// Assign the reference
176 				release->references = 1;
177 
178 				// Determine the ptr
179 				ptr = ( char * )release + sizeof( struct mlt_release_s );
180 			}
181 		}
182 
183 		// Unlock the pool
184 		pthread_mutex_unlock( &self->lock );
185 	}
186 
187 	// Return the generated release object
188 	return ptr;
189 }
190 
191 /** Return an item to the pool.
192  *
193  * \private \memberof mlt_pool_s
194  * \param ptr an opaque pointer
195  */
196 
pool_return(void * ptr)197 static void pool_return( void *ptr )
198 {
199 	// Sanity checks
200 	if ( ptr != NULL )
201 	{
202 		// Get the release pointer
203 		mlt_release that = ( void * )(( char * )ptr - sizeof( struct mlt_release_s ));
204 
205 		// Get the pool
206 		mlt_pool self = that->pool;
207 
208 		if ( self != NULL )
209 		{
210 			// Lock the pool
211 			pthread_mutex_lock( &self->lock );
212 
213 			// Push the that back back on to the stack
214 			mlt_deque_push_back( self->stack, ptr );
215 
216 			// Unlock the pool
217 			pthread_mutex_unlock( &self->lock );
218 
219 			return;
220 		}
221 
222 		// Free the release itself
223 		mlt_free( ( char * )ptr - sizeof( struct mlt_release_s ) );
224 	}
225 }
226 
227 /** Destroy a pool.
228  *
229  * \private \memberof mlt_pool_s
230  * \param self a pool
231  */
232 
pool_close(mlt_pool self)233 static void pool_close( mlt_pool self )
234 {
235 	if ( self != NULL )
236 	{
237 		// We need to free up all items in the pool
238 		void *release = NULL;
239 
240 		// Iterate through the stack until depleted
241 		while ( ( release = mlt_deque_pop_back( self->stack ) ) != NULL )
242 		{
243 			// We'll free this item now
244 			mlt_free( ( char * )release - sizeof( struct mlt_release_s ) );
245 		}
246 
247 		// We can now close the stack
248 		mlt_deque_close( self->stack );
249 
250 		// Destroy the mutex
251 		pthread_mutex_destroy( &self->lock );
252 
253 		// Close the pool
254 		free( self );
255 	}
256 }
257 
258 /** Initialise the global pool.
259  *
260  * \public \memberof mlt_pool_s
261  */
262 
mlt_pool_init()263 void mlt_pool_init( )
264 {
265 	// Loop variable used to create the pools
266 	int i = 0;
267 
268 	// Create the pools
269 	pools = mlt_properties_new( );
270 
271 	// Create the pools
272 	for ( i = 8; i < 31; i ++ )
273 	{
274 		// Each properties item needs a name
275 		char name[ 32 ];
276 
277 		// Construct a pool
278 		mlt_pool pool = pool_init( 1 << i );
279 
280 		// Generate a name
281 		sprintf( name, "%d", i );
282 
283 		// Register with properties
284 		mlt_properties_set_data( pools, name, pool, 0, ( mlt_destructor )pool_close, NULL );
285 	}
286 }
287 
288 /** Allocate size bytes from the pool.
289  *
290  * \public \memberof mlt_pool_s
291  * \param size the number of bytes
292  */
293 
mlt_pool_alloc(int size)294 void *mlt_pool_alloc( int size )
295 {
296 	// This will be used to obtain the pool to use
297 	mlt_pool pool = NULL;
298 
299 	// Determines the index of the pool to use
300 	int index = 8;
301 
302 	// Minimum size pooled is 256 bytes
303 	size += sizeof( struct mlt_release_s );
304 	while ( ( 1 << index ) < size )
305 		index ++;
306 
307 	// Now get the pool at the index
308 	pool = mlt_properties_get_data_at( pools, index - 8, NULL );
309 
310 	// Now get the real item
311 	return pool_fetch( pool );
312 }
313 
314 /** Allocate size bytes from the pool.
315  *
316  * \public \memberof mlt_pool_s
317  * \param ptr an opaque pointer - can be in the pool or a new block to allocate
318  * \param size the number of bytes
319  */
320 
mlt_pool_realloc(void * ptr,int size)321 void *mlt_pool_realloc( void *ptr, int size )
322 {
323 	// Result to return
324 	void *result = NULL;
325 
326 	// Check if we actually have an address
327 	if ( ptr != NULL )
328 	{
329 		// Get the release pointer
330 		mlt_release that = ( void * )(( char * )ptr - sizeof( struct mlt_release_s ));
331 
332 		// If the current pool this ptr belongs to is big enough
333 		if ( size > that->pool->size - sizeof( struct mlt_release_s ) )
334 		{
335 			// Allocate
336 			result = mlt_pool_alloc( size );
337 
338 			// Copy
339 			memcpy( result, ptr, that->pool->size - sizeof( struct mlt_release_s ) );
340 
341 			// Release
342 			mlt_pool_release( ptr );
343 		}
344 		else
345 		{
346 			// Nothing to do
347 			result = ptr;
348 		}
349 	}
350 	else
351 	{
352 		// Simply allocate
353 		result = mlt_pool_alloc( size );
354 	}
355 
356 	return result;
357 }
358 
359 /** Purge unused items in the pool.
360  *
361  * A form of garbage collection.
362  * \public \memberof mlt_pool_s
363  */
364 
mlt_pool_purge()365 void mlt_pool_purge( )
366 {
367 	int i = 0;
368 
369 	// For each pool
370 	for ( i = 0; i < mlt_properties_count( pools ); i ++ )
371 	{
372 		// Get the pool
373 		mlt_pool self = mlt_properties_get_data_at( pools, i, NULL );
374 
375 		// Pointer to unused memory
376 		void *release = NULL;
377 
378 		// Lock the pool
379 		pthread_mutex_lock( &self->lock );
380 
381 		// We'll free all unused items now
382 		while ( ( release = mlt_deque_pop_back( self->stack ) ) != NULL )
383 		{
384 			mlt_free( ( char * )release - sizeof( struct mlt_release_s ) );
385 			self->count--;
386 		}
387 
388 		// Unlock the pool
389 		pthread_mutex_unlock( &self->lock );
390 	}
391 }
392 
393 /** Release the allocated memory.
394  *
395  * \public \memberof mlt_pool_s
396  * \param release an opaque pointer of a block in the pool
397  */
398 
mlt_pool_release(void * release)399 void mlt_pool_release( void *release )
400 {
401 	// Return to the pool
402 	pool_return( release );
403 }
404 
405 /** Close the pool.
406  *
407  * \public \memberof mlt_pool_s
408  */
409 
mlt_pool_close()410 void mlt_pool_close( )
411 {
412 #ifdef _MLT_POOL_CHECKS_
413 	mlt_pool_stat( );
414 #endif
415 
416 	// Close the properties
417 	mlt_properties_close( pools );
418 }
419 
mlt_pool_stat()420 void mlt_pool_stat( )
421 {
422 	// Stats dump
423 	uint64_t allocated = 0, used = 0, s;
424 	int i = 0, c = mlt_properties_count( pools );
425 
426 	mlt_log( NULL, MLT_LOG_VERBOSE, "%s: count %d\n", __FUNCTION__, c);
427 
428 	for ( i = 0; i < c; i ++ )
429 	{
430 		mlt_pool pool = mlt_properties_get_data_at( pools, i, NULL );
431 		if ( pool->count )
432 			mlt_log_verbose( NULL, "%s: size %d allocated %d returned %d %c\n", __FUNCTION__,
433 				pool->size, pool->count, mlt_deque_count( pool->stack ),
434 				pool->count !=  mlt_deque_count( pool->stack ) ? '*' : ' ' );
435 		s = pool->size; s *= pool->count; allocated += s;
436 		s = pool->count - mlt_deque_count( pool->stack ); s *= pool->size; used += s;
437 	}
438 
439 	mlt_log_verbose( NULL, "%s: allocated %"PRIu64" bytes, used %"PRIu64" bytes \n",
440 		__FUNCTION__, allocated, used );
441 }
442 
443 #endif // NO_MLT_POOL
444