OSDN Git Service

cd348c863dca26e14f9324bff4daa4bfb9e90cdd
[handbrake-jp/handbrake-jp-git.git] / libhb / fifo.c
1 /* $Id: fifo.c,v 1.17 2005/10/15 18:05:03 titer Exp $
2
3    This file is part of the HandBrake source code.
4    Homepage: <http://handbrake.fr/>.
5    It may be used under the terms of the GNU General Public License. */
6
7 #include "hb.h"
8
9 #ifndef SYS_DARWIN
10 #include <malloc.h>
11 #endif
12
13 #define FIFO_TIMEOUT 200
14
15 /* Fifo */
16 struct hb_fifo_s
17 {
18     hb_lock_t    * lock;
19     hb_cond_t    * cond_full;
20     int            wait_full;
21     hb_cond_t    * cond_empty;
22     int            wait_empty;
23     uint32_t       capacity;
24     uint32_t       thresh;
25     uint32_t       size;
26     uint32_t       buffer_size;
27     hb_buffer_t  * first;
28     hb_buffer_t  * last;
29 };
30
31 /* we round the requested buffer size up to the next power of 2 so there can
32  * be at most 32 possible pools when the size is a 32 bit int. To avoid a lot
33  * of slow & error-prone run-time checking we allow for all 32. */
34 #define MAX_BUFFER_POOLS  32
35 /* the buffer pool only exists to avoid the two malloc and two free calls that
36  * it would otherwise take to allocate & free a buffer. but we don't want to
37  * tie up a lot of memory in the pool because this allocator isn't as general
38  * as malloc so memory tied up here puts more pressure on the malloc pool.
39  * A pool of 16 elements will avoid 94% of the malloc/free calls without wasting
40  * too much memory. */
41 #define BUFFER_POOL_MAX_ELEMENTS 32
42
43 struct hb_buffer_pools_s
44 {
45     int64_t allocated;
46     hb_lock_t *lock;
47     hb_fifo_t *pool[MAX_BUFFER_POOLS];
48 } buffers;
49
50
51 void hb_buffer_pool_init( void )
52 {
53     buffers.lock = hb_lock_init();
54     buffers.allocated = 0;
55
56     /* we allocate pools for sizes 2^10 through 2^25. requests larger than
57      * 2^25 will get passed through to malloc. */
58     int i;
59     for ( i = 10; i < 26; ++i )
60     {
61         buffers.pool[i] = hb_fifo_init(BUFFER_POOL_MAX_ELEMENTS, 1);
62         buffers.pool[i]->buffer_size = 1 << i;
63     }
64     /* requests smaller than 2^10 are satisfied from the 2^10 pool. */
65     for ( i = 1; i < 10; ++i )
66     {
67         buffers.pool[i] = buffers.pool[10];
68     }
69 }
70
71 void hb_buffer_pool_free( void )
72 {
73     int i;
74     int count;
75     int64_t freed = 0;
76     hb_buffer_t *b;
77
78     hb_lock(buffers.lock);
79
80     for( i = 10; i < 26; ++i)
81     {
82         count = 0;
83         while( ( b = hb_fifo_get(buffers.pool[i]) ) )
84         {
85             freed += b->alloc;
86             if( b->data )
87             {
88                 free( b->data );
89             }
90             free( b );
91             count++;
92         }
93         if ( count )
94         {
95             hb_deep_log( 2, "Freed %d buffers of size %d", count,
96                     buffers.pool[i]->buffer_size);
97         }
98     }
99
100     hb_deep_log( 2, "Allocated %"PRId64" bytes of buffers on this pass and Freed %"PRId64" bytes, "
101            "%"PRId64" bytes leaked", buffers.allocated, freed, buffers.allocated - freed);
102     buffers.allocated = 0;
103     hb_unlock(buffers.lock);
104 }
105
106 static hb_fifo_t *size_to_pool( int size )
107 {
108     int i;
109     for ( i = 0; i < 30; ++i )
110     {
111         if ( size <= (1 << i) )
112         {
113             return buffers.pool[i];
114         }
115     }
116     return NULL;
117 }
118
119 hb_buffer_t * hb_buffer_init( int size )
120 {
121     hb_buffer_t * b;
122     hb_fifo_t *buffer_pool = size_to_pool( size );
123
124     if( buffer_pool )
125     {
126         b = hb_fifo_get( buffer_pool );
127
128         if( b )
129         {
130             /*
131              * Zero the contents of the buffer, would be nice if we
132              * didn't have to do this.
133              */
134             uint8_t *data = b->data;
135             memset( b, 0, sizeof(hb_buffer_t) );
136             b->alloc = buffer_pool->buffer_size;
137             b->size = size;
138             b->data = data;
139             return( b );
140         }
141     }
142
143     /*
144      * No existing buffers, create a new one
145      */
146     if( !( b = calloc( sizeof( hb_buffer_t ), 1 ) ) )
147     {
148         hb_log( "out of memory" );
149         return NULL;
150     }
151
152     b->size  = size;
153     b->alloc  = buffer_pool? buffer_pool->buffer_size : size;
154
155     if (size)
156     {
157 #if defined( SYS_DARWIN ) || defined( SYS_FREEBSD ) || defined( SYS_MINGW )
158         b->data  = malloc( b->alloc );
159 #elif defined( SYS_CYGWIN )
160         /* FIXME */
161         b->data  = malloc( b->alloc + 17 );
162 #else
163         b->data  = memalign( 16, b->alloc );
164 #endif
165         if( !b->data )
166         {
167             hb_log( "out of memory" );
168             free( b );
169             return NULL;
170         }
171         hb_lock(buffers.lock);
172         buffers.allocated += b->alloc;
173         hb_unlock(buffers.lock);
174     }
175     return b;
176 }
177
178 void hb_buffer_realloc( hb_buffer_t * b, int size )
179 {
180     if ( size > b->alloc )
181     {
182         uint32_t orig = b->alloc;
183         size = size_to_pool( size )->buffer_size;
184         b->data  = realloc( b->data, size );
185         b->alloc = size;
186
187         hb_lock(buffers.lock);
188         buffers.allocated += size - orig;
189         hb_unlock(buffers.lock);
190     }
191 }
192
193 // Frees the specified buffer list.
194 void hb_buffer_close( hb_buffer_t ** _b )
195 {
196     hb_buffer_t * b = *_b;
197
198     while( b )
199     {
200         hb_buffer_t * next = b->next;
201         hb_fifo_t *buffer_pool = size_to_pool( b->alloc );
202
203         b->next = NULL;
204
205         if( buffer_pool && b->data && !hb_fifo_is_full( buffer_pool ) )
206         {
207             hb_fifo_push_head( buffer_pool, b );
208             b = next;
209             continue;
210         }
211         // either the pool is full or this size doesn't use a pool
212         // free the buf 
213         if( b->data )
214         {
215             free( b->data );
216             hb_lock(buffers.lock);
217             buffers.allocated -= b->alloc;
218             hb_unlock(buffers.lock);
219         }
220         free( b );
221         b = next;
222     }
223     *_b = NULL;
224 }
225
226 void hb_buffer_copy_settings( hb_buffer_t * dst, const hb_buffer_t * src )
227 {
228     dst->start     = src->start;
229     dst->stop      = src->stop;
230     dst->new_chap  = src->new_chap;
231     dst->frametype = src->frametype;
232     dst->flags     = src->flags;
233 }
234
235 hb_fifo_t * hb_fifo_init( int capacity, int thresh )
236 {
237     hb_fifo_t * f;
238     f             = calloc( sizeof( hb_fifo_t ), 1 );
239     f->lock       = hb_lock_init();
240     f->cond_full  = hb_cond_init();
241     f->cond_empty = hb_cond_init();
242     f->capacity   = capacity;
243     f->thresh     = thresh;
244     f->buffer_size = 0;
245     return f;
246 }
247
248 int hb_fifo_size_bytes( hb_fifo_t * f )
249 {
250     int ret = 0;
251     hb_buffer_t * link;
252
253     hb_lock( f->lock );
254     link = f->first;
255     while ( link )
256     {
257         ret += link->size;
258         link = link->next;
259     }
260     hb_unlock( f->lock );
261
262     return ret;
263 }
264
265 int hb_fifo_size( hb_fifo_t * f )
266 {
267     int ret;
268
269     hb_lock( f->lock );
270     ret = f->size;
271     hb_unlock( f->lock );
272
273     return ret;
274 }
275
276 int hb_fifo_is_full( hb_fifo_t * f )
277 {
278     int ret;
279
280     hb_lock( f->lock );
281     ret = ( f->size >= f->capacity );
282     hb_unlock( f->lock );
283
284     return ret;
285 }
286
287 float hb_fifo_percent_full( hb_fifo_t * f )
288 {
289     float ret;
290
291     hb_lock( f->lock );
292     ret = f->size / f->capacity;
293     hb_unlock( f->lock );
294
295     return ret;
296 }
297
298 // Pulls the first packet out of this FIFO, blocking until such a packet is available.
299 // Returns NULL if this FIFO has been closed or flushed.
300 hb_buffer_t * hb_fifo_get_wait( hb_fifo_t * f )
301 {
302     hb_buffer_t * b;
303
304     hb_lock( f->lock );
305     if( f->size < 1 )
306     {
307         f->wait_empty = 1;
308         hb_cond_timedwait( f->cond_empty, f->lock, FIFO_TIMEOUT );
309         if( f->size < 1 )
310         {
311             hb_unlock( f->lock );
312             return NULL;
313         }
314     }
315     b         = f->first;
316     f->first  = b->next;
317     b->next   = NULL;
318     f->size  -= 1;
319     if( f->wait_full && f->size == f->capacity - f->thresh )
320     {
321         f->wait_full = 0;
322         hb_cond_signal( f->cond_full );
323     }
324     hb_unlock( f->lock );
325
326     return b;
327 }
328
329 // Pulls a packet out of this FIFO, or returns NULL if no packet is available.
330 hb_buffer_t * hb_fifo_get( hb_fifo_t * f )
331 {
332     hb_buffer_t * b;
333
334     hb_lock( f->lock );
335     if( f->size < 1 )
336     {
337         hb_unlock( f->lock );
338         return NULL;
339     }
340     b         = f->first;
341     f->first  = b->next;
342     b->next   = NULL;
343     f->size  -= 1;
344     if( f->wait_full && f->size == f->capacity - f->thresh )
345     {
346         f->wait_full = 0;
347         hb_cond_signal( f->cond_full );
348     }
349     hb_unlock( f->lock );
350
351     return b;
352 }
353
354 hb_buffer_t * hb_fifo_see_wait( hb_fifo_t * f )
355 {
356     hb_buffer_t * b;
357
358     hb_lock( f->lock );
359     if( f->size < 1 )
360     {
361         f->wait_empty = 1;
362         hb_cond_timedwait( f->cond_empty, f->lock, FIFO_TIMEOUT );
363         if( f->size < 1 )
364         {
365             hb_unlock( f->lock );
366             return NULL;
367         }
368     }
369     b = f->first;
370     hb_unlock( f->lock );
371
372     return b;
373 }
374
375 // Returns the first packet in the specified FIFO.
376 // If the FIFO is empty, returns NULL.
377 hb_buffer_t * hb_fifo_see( hb_fifo_t * f )
378 {
379     hb_buffer_t * b;
380
381     hb_lock( f->lock );
382     if( f->size < 1 )
383     {
384         hb_unlock( f->lock );
385         return NULL;
386     }
387     b = f->first;
388     hb_unlock( f->lock );
389
390     return b;
391 }
392
393 hb_buffer_t * hb_fifo_see2( hb_fifo_t * f )
394 {
395     hb_buffer_t * b;
396
397     hb_lock( f->lock );
398     if( f->size < 2 )
399     {
400         hb_unlock( f->lock );
401         return NULL;
402     }
403     b = f->first->next;
404     hb_unlock( f->lock );
405
406     return b;
407 }
408
409 // Waits until the specified FIFO is no longer full or until FIFO_TIMEOUT milliseconds have elapsed.
410 // Returns whether the FIFO is non-full upon return.
411 int hb_fifo_full_wait( hb_fifo_t * f )
412 {
413     int result;
414
415     hb_lock( f->lock );
416     if( f->size >= f->capacity )
417     {
418         f->wait_full = 1;
419         hb_cond_timedwait( f->cond_full, f->lock, FIFO_TIMEOUT );
420     }
421     result = ( f->size < f->capacity );
422     hb_unlock( f->lock );
423     return result;
424 }
425
426 // Pushes the specified buffer onto the specified FIFO,
427 // blocking until the FIFO has space available.
428 void hb_fifo_push_wait( hb_fifo_t * f, hb_buffer_t * b )
429 {
430     if( !b )
431     {
432         return;
433     }
434
435     hb_lock( f->lock );
436     if( f->size >= f->capacity )
437     {
438         f->wait_full = 1;
439         hb_cond_timedwait( f->cond_full, f->lock, FIFO_TIMEOUT );
440     }
441     if( f->size > 0 )
442     {
443         f->last->next = b;
444     }
445     else
446     {
447         f->first = b;
448     }
449     f->last  = b;
450     f->size += 1;
451     while( f->last->next )
452     {
453         f->size += 1;
454         f->last  = f->last->next;
455     }
456     if( f->wait_empty && f->size >= f->thresh )
457     {
458         f->wait_empty = 0;
459         hb_cond_signal( f->cond_empty );
460     }
461     hb_unlock( f->lock );
462 }
463
464 // Appends the specified packet list to the end of the specified FIFO.
465 void hb_fifo_push( hb_fifo_t * f, hb_buffer_t * b )
466 {
467     if( !b )
468     {
469         return;
470     }
471
472     hb_lock( f->lock );
473     if( f->size > 0 )
474     {
475         f->last->next = b;
476     }
477     else
478     {
479         f->first = b;
480     }
481     f->last  = b;
482     f->size += 1;
483     while( f->last->next )
484     {
485         f->size += 1;
486         f->last  = f->last->next;
487     }
488     if( f->wait_empty && f->size >= f->thresh )
489     {
490         f->wait_empty = 0;
491         hb_cond_signal( f->cond_empty );
492     }
493     hb_unlock( f->lock );
494 }
495
496 // Prepends the specified packet list to the start of the specified FIFO.
497 void hb_fifo_push_head( hb_fifo_t * f, hb_buffer_t * b )
498 {
499     hb_buffer_t * tmp;
500     uint32_t      size = 0;
501
502     if( !b )
503     {
504         return;
505     }
506
507     hb_lock( f->lock );
508
509     /*
510      * If there are a chain of buffers prepend the lot
511      */
512     tmp = b;
513     while( tmp->next )
514     {
515         tmp = tmp->next;
516         size += 1;
517     }
518
519     if( f->size > 0 )
520     {
521         tmp->next = f->first;
522     } 
523     else
524     {
525         f->last = tmp;
526     }
527
528     f->first = b;
529     f->size += ( size + 1 );
530
531     hb_unlock( f->lock );
532 }
533
534 // Pushes a list of packets onto the specified FIFO as a single element.
535 void hb_fifo_push_list_element( hb_fifo_t *fifo, hb_buffer_t *buffer_list )
536 {
537     hb_buffer_t *container = hb_buffer_init( 0 );
538     // XXX: Using an arbitrary hb_buffer_t pointer (other than 'next')
539     //      to carry the list inside a single "container" buffer
540     container->next_subpicture = buffer_list;
541     
542     hb_fifo_push( fifo, container );
543 }
544
545 // Removes a list of packets from the specified FIFO that were stored as a single element.
546 hb_buffer_t *hb_fifo_get_list_element( hb_fifo_t *fifo )
547 {
548     hb_buffer_t *container = hb_fifo_get( fifo );
549     // XXX: Using an arbitrary hb_buffer_t pointer (other than 'next')
550     //      to carry the list inside a single "container" buffer
551     hb_buffer_t *buffer_list = container->next_subpicture;
552     hb_buffer_close( &container );
553     
554     return buffer_list;
555 }
556
557 void hb_fifo_close( hb_fifo_t ** _f )
558 {
559     hb_fifo_t   * f = *_f;
560     hb_buffer_t * b;
561
562     hb_deep_log( 2, "fifo_close: trashing %d buffer(s)", hb_fifo_size( f ) );
563     while( ( b = hb_fifo_get( f ) ) )
564     {
565         hb_buffer_close( &b );
566     }
567
568     hb_lock_close( &f->lock );
569     hb_cond_close( &f->cond_empty );
570     hb_cond_close( &f->cond_full );
571     free( f );
572
573     *_f = NULL;
574 }
575
576 void hb_fifo_flush( hb_fifo_t * f )
577 {
578     hb_buffer_t * b;
579
580     while( ( b = hb_fifo_get( f ) ) )
581     {
582         hb_buffer_close( &b );
583     }
584     hb_lock( f->lock );
585     hb_cond_signal( f->cond_empty );
586     hb_cond_signal( f->cond_full );
587     hb_unlock( f->lock );
588
589 }
590