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