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