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