Index: libhb/bits.h =================================================================== --- libhb/bits.h (revision 0) +++ libhb/bits.h (revision 0) @@ -0,0 +1,86 @@ +/* $Id$ + + This file is part of the HandBrake source code. + Homepage: . + It may be used under the terms of the GNU General Public License. */ + +#ifndef HB_BITS_H +#define HB_BITS_H + +static inline int +allbits_set(uint32_t *bitmap, int num_words) +{ + unsigned int i; + for( i = 0; i < num_words; i++ ) + { + if( bitmap[i] != 0xFFFFFFFF ) + return (0); + } + return (1); +} + +static inline int +bit_is_set( uint32_t *bit_map, int bit_pos ) +{ + return( ( bit_map[bit_pos >> 5] & (0x1 << (bit_pos & 0x1F) ) ) != 0 ); +} + +static inline int +bit_is_clear( uint32_t *bit_map, int bit_pos ) +{ + return( ( bit_map[bit_pos >> 5] & (0x1 << (bit_pos & 0x1F) ) ) == 0); +} + +static inline void +bit_set( uint32_t *bit_map, int bit_pos ) +{ + bit_map[bit_pos >> 5] |= 0x1 << (bit_pos & 0x1F); +} + +static inline void +bit_clear(uint32_t *bit_map, int bit_pos) +{ + bit_map[bit_pos >> 5] &= ~( 0x1 << ( bit_pos & 0x1F ) ); +} + +static inline void +bit_nclear(uint32_t *bit_map, int start_pos, int stop_pos) +{ + int start_word = start_pos >> 5; + int stop_word = stop_pos >> 5; + + if ( start_word == stop_word ) + { + bit_map[start_word] &= ((0xFFFFFFFF >> (32 - (start_pos & 0x1F))) + | (0xFFFFFFFF << ((stop_pos & 0x1F) + 1))); + } + else + { + bit_map[start_word] &= 0xFFFFFFFF >> (32 - (start_pos & 0x1F)); + while (++start_word < stop_word) + bit_map[start_word] = 0; + bit_map[stop_word] &= 0xFFFFFFFF << ((stop_pos & 0x1F) + 1); + } +} + +static inline void +bit_nset(uint32_t *bit_map, int start_pos, int stop_pos) +{ + int start_word = start_pos >> 5; + int stop_word = stop_pos >> 5; + + if ( start_word == stop_word ) + { + bit_map[start_word] |= ((0xFFFFFFFF << (start_pos & 0x1F)) + & (0xFFFFFFFF >> (31 - (stop_pos & 0x1F)))); + } + else + { + bit_map[start_word] |= 0xFFFFFFFF << (start_pos & 0x1F); + while (++start_word < stop_word) + bit_map[start_word] = 0xFFFFFFFF; + bit_map[stop_word] |= 0xFFFFFFFF >> (31 - (stop_pos & 0x1F)); + } +} + +#endif /* HB_BITS_H */ Index: libhb/rotate.c =================================================================== --- libhb/rotate.c (revision 3169) +++ libhb/rotate.c (working copy) @@ -2,6 +2,7 @@ #include "hb.h" #include "hbffmpeg.h" //#include "mpeg2dec/mpeg2.h" +#include "taskset.h" #define MODE_DEFAULT 3 // Mode 1: Flip vertically (y0 becomes yN and yN becomes y0) @@ -10,7 +11,6 @@ typedef struct rotate_arguments_s { uint8_t **dst; - int stop; } rotate_arguments_t; struct hb_filter_private_s @@ -25,9 +25,7 @@ int cpu_count; - hb_thread_t ** rotate_threads; // Threads for Rotate - one per CPU - hb_lock_t ** rotate_begin_lock; // Thread has work - hb_lock_t ** rotate_complete_lock; // Thread has completed work + taskset_t rotate_taskset; // Threads for Rotate - one per CPU rotate_arguments_t *rotate_arguments; // Arguments to thread for work AVPicture pic_in; @@ -111,27 +109,25 @@ while( run ) { /* - * Wait here until there is work to do. hb_lock() blocks until - * render releases it to say that there is more work to do. + * Wait here until there is work to do. */ - hb_lock( pv->rotate_begin_lock[segment] ); + taskset_thread_wait4start( &pv->rotate_taskset, segment ); - rotate_work = &pv->rotate_arguments[segment]; - - if( rotate_work->stop ) + if( taskset_thread_stop( &pv->rotate_taskset, segment ) ) { /* * No more work to do, exit this thread. */ run = 0; - continue; + goto report_completion; } + rotate_work = &pv->rotate_arguments[segment]; if( rotate_work->dst == NULL ) { hb_error( "Thread started when no work available" ); hb_snooze(500); - continue; + goto report_completion; } /* @@ -175,12 +171,13 @@ pv ); } } + +report_completion: /* * Finished this segment, let everyone know. */ - hb_unlock( pv->rotate_complete_lock[segment] ); + taskset_thread_complete( &pv->rotate_taskset, segment ); } - free( thread_args_v ); } @@ -203,28 +200,12 @@ * Setup the work for this plane. */ pv->rotate_arguments[segment].dst = dst; - - /* - * Let the thread for this plane know that we've setup work - * for it by releasing the begin lock (ensuring that the - * complete lock is already locked so that we block when - * we try to lock it again below). - */ - hb_lock( pv->rotate_complete_lock[segment] ); - hb_unlock( pv->rotate_begin_lock[segment] ); } /* - * Wait until all three threads have completed by trying to get - * the complete lock that we locked earlier for each thread, which - * will block until that thread has completed the work on that - * plane. + * Allow the taskset threads to make one pass over the data. */ - for( segment = 0; segment < pv->cpu_count; segment++ ) - { - hb_lock( pv->rotate_complete_lock[segment] ); - hb_unlock( pv->rotate_complete_lock[segment] ); - } + taskset_cycle( &pv->rotate_taskset ); /* * Entire frame is now rotated. @@ -268,44 +249,34 @@ pv->cpu_count = hb_get_cpu_count(); - /* - * Create threads and locks. + * Create rotate taskset. */ - pv->rotate_threads = malloc( sizeof( hb_thread_t* ) * pv->cpu_count ); - pv->rotate_begin_lock = malloc( sizeof( hb_lock_t * ) * pv->cpu_count ); - pv->rotate_complete_lock = malloc( sizeof( hb_lock_t * ) * pv->cpu_count ); pv->rotate_arguments = malloc( sizeof( rotate_arguments_t ) * pv->cpu_count ); + if( pv->rotate_arguments == NULL || + taskset_init( &pv->rotate_taskset, /*thread_count*/pv->cpu_count, + sizeof( rotate_thread_arg_t ) ) == 0 ) + { + hb_error( "rotate could not initialize taskset" ); + } int i; for( i = 0; i < pv->cpu_count; i++ ) { rotate_thread_arg_t *thread_args; - thread_args = malloc( sizeof( rotate_thread_arg_t ) ); + thread_args = taskset_thread_args( &pv->rotate_taskset, i ); - if( thread_args ) { - thread_args->pv = pv; - thread_args->segment = i; + thread_args->pv = pv; + thread_args->segment = i; + pv->rotate_arguments[i].dst = NULL; - pv->rotate_begin_lock[i] = hb_lock_init(); - pv->rotate_complete_lock[i] = hb_lock_init(); - - /* - * Important to start off with the threads locked waiting - * on input. - */ - hb_lock( pv->rotate_begin_lock[i] ); - - pv->rotate_arguments[i].stop = 0; - pv->rotate_arguments[i].dst = NULL; - - pv->rotate_threads[i] = hb_thread_init( "rotate_filter_segment", - rotate_filter_thread, - thread_args, - HB_NORMAL_PRIORITY ); - } else { - hb_error( "rotate could not create threads" ); + if( taskset_thread_spawn( &pv->rotate_taskset, i, + "rotate_filter_segment", + rotate_filter_thread, + HB_NORMAL_PRIORITY ) == 0 ) + { + hb_error( "rotate could not spawn thread" ); } } @@ -329,26 +300,11 @@ hb_buffer_close( &pv->buf_settings ); } - int i; - for( i = 0; i < pv->cpu_count; i++) - { - /* - * Tell each rotate thread to stop, and then cleanup. - */ - pv->rotate_arguments[i].stop = 1; - hb_unlock( pv->rotate_begin_lock[i] ); + taskset_fini( &pv->rotate_taskset ); - hb_thread_close( &pv->rotate_threads[i] ); - hb_lock_close( &pv->rotate_begin_lock[i] ); - hb_lock_close( &pv->rotate_complete_lock[i] ); - } - /* * free memory for rotate structs */ - free( pv->rotate_threads ); - free( pv->rotate_begin_lock ); - free( pv->rotate_complete_lock ); free( pv->rotate_arguments ); free( pv ); Index: libhb/ports.c =================================================================== --- libhb/ports.c (revision 3169) +++ libhb/ports.c (working copy) @@ -290,20 +290,20 @@ ***********************************************************************/ struct hb_thread_s { - char * name; - int priority; - void (* function) ( void * ); - void * arg; + char * name; + int priority; + thread_func_t * function; + void * arg; - hb_lock_t * lock; - int exited; + hb_lock_t * lock; + int exited; #if defined( SYS_BEOS ) - thread_id thread; + thread_id thread; #elif USE_PTHREAD - pthread_t thread; + pthread_t thread; //#elif defined( SYS_CYGWIN ) -// HANDLE thread; +// HANDLE thread; #endif }; @@ -342,7 +342,7 @@ { hb_thread_t * t = (hb_thread_t *) _t; -#if defined( SYS_DARWIN ) +#if defined( SYS_DARWIN ) || defined( SYS_FREEBSD ) /* Set the thread priority */ struct sched_param param; memset( ¶m, 0, sizeof( struct sched_param ) ); @@ -372,7 +372,7 @@ * arg: argument of the routine * priority: HB_LOW_PRIORITY or HB_NORMAL_PRIORITY ***********************************************************************/ -hb_thread_t * hb_thread_init( char * name, void (* function)(void *), +hb_thread_t * hb_thread_init( const char * name, void (* function)(void *), void * arg, int priority ) { hb_thread_t * t = calloc( sizeof( hb_thread_t ), 1 ); @@ -485,7 +485,7 @@ pthread_mutexattr_init(&mta); -#if defined( SYS_CYGWIN ) +#if defined( SYS_CYGWIN ) || defined( SYS_FREEBSD ) pthread_mutexattr_settype(&mta, PTHREAD_MUTEX_NORMAL); #endif @@ -562,6 +562,9 @@ { hb_cond_t * c = calloc( sizeof( hb_cond_t ), 1 ); + if( c == NULL ) + return NULL; + #if defined( SYS_BEOS ) c->thread = -1; #elif USE_PTHREAD Index: libhb/ports.h =================================================================== --- libhb/ports.h (revision 3169) +++ libhb/ports.h (working copy) @@ -59,7 +59,8 @@ # define HB_NORMAL_PRIORITY 0 #endif -hb_thread_t * hb_thread_init( char * name, void (* function)(void *), +typedef void (thread_func_t)(void *); +hb_thread_t * hb_thread_init( const char * name, thread_func_t *function, void * arg, int priority ); void hb_thread_close( hb_thread_t ** ); int hb_thread_has_exited( hb_thread_t * ); Index: libhb/taskset.c =================================================================== --- libhb/taskset.c (revision 0) +++ libhb/taskset.c (revision 0) @@ -0,0 +1,231 @@ +/* $Id$ + + This file is part of the HandBrake source code. + Homepage: . + It may be used under the terms of the GNU General Public License. */ + +#include "hb.h" +#include "ports.h" +#include "taskset.h" + +int +taskset_init( taskset_t *ts, int thread_count, size_t arg_size ) +{ + int init_step; + + init_step = 0; + memset( ts, 0, sizeof( *ts ) ); + ts->thread_count = thread_count; + ts->arg_size = arg_size; + ts->bitmap_elements = ( ts->thread_count + 31 ) / 32; + ts->task_threads = malloc( sizeof( hb_thread_t* ) * ts->thread_count ); + if( ts->task_threads == NULL ) + goto fail; + init_step++; + + if( arg_size != 0 ) + { + ts->task_threads_args = malloc( arg_size * ts->thread_count ); + if( ts->task_threads == NULL ) + goto fail; + } + init_step++; + + ts->task_begin_bitmap = malloc( sizeof( uint32_t ) * ts->bitmap_elements ); + if( ts->task_begin_bitmap == NULL ) + goto fail; + init_step++; + + ts->task_complete_bitmap = malloc( sizeof( uint32_t ) * ts->bitmap_elements ); + if( ts->task_complete_bitmap == NULL ) + goto fail; + init_step++; + + ts->task_stop_bitmap = malloc( sizeof( uint32_t ) * ts->bitmap_elements ); + if( ts->task_stop_bitmap == NULL ) + goto fail; + init_step++; + + ts->task_cond_lock = hb_lock_init(); + if( ts->task_cond_lock == NULL) + goto fail; + init_step++; + + ts->task_begin = hb_cond_init(); + if( ts->task_begin == NULL) + goto fail; + init_step++; + + ts->task_complete = hb_cond_init(); + if( ts->task_complete == NULL) + goto fail; + init_step++; + + /* + * Initialize all arg data to 0. + */ + memset(ts->task_threads_args, 0, ts->arg_size * ts->thread_count ); + + /* + * Inialize bitmaps to all bits set. This means that any unused bits + * in the bitmap are already in the "condition satisfied" state allowing + * us to test the bitmap 32bits at a time without having to mask off + * the end. + */ + memset(ts->task_begin_bitmap, 0xFF, sizeof( uint32_t ) * ts->bitmap_elements ); + memset(ts->task_complete_bitmap, 0xFF, sizeof( uint32_t ) * ts->bitmap_elements ); + memset(ts->task_stop_bitmap, 0, sizeof( uint32_t ) * ts->bitmap_elements ); + + /* + * Important to start off with the threads locked waiting + * on input, no work completed, and not asked to stop. + */ + bit_nclear( ts->task_begin_bitmap, 0, ts->thread_count - 1 ); + bit_nclear( ts->task_complete_bitmap, 0, ts->thread_count - 1 ); + bit_nclear( ts->task_stop_bitmap, 0, ts->thread_count - 1 ); + return (1); + +fail: + switch (init_step) + { + default: + hb_cond_close( &ts->task_complete ); + /* FALL THROUGH */ + case 7: + hb_cond_close( &ts->task_begin ); + /* FALL THROUGH */ + case 6: + hb_lock_close( &ts->task_cond_lock ); + /* FALL THROUGH */ + case 5: + free( ts->task_stop_bitmap ); + /* FALL THROUGH */ + case 4: + free( ts->task_complete_bitmap ); + /* FALL THROUGH */ + case 3: + free( ts->task_begin_bitmap ); + /* FALL THROUGH */ + case 2: + if( ts->task_threads_args == NULL ) + free( ts->task_threads_args ); + /* FALL THROUGH */ + case 1: + free( ts->task_threads ); + /* FALL THROUGH */ + case 0: + break; + } + return (0); +} + +int +taskset_thread_spawn( taskset_t *ts, int thr_idx, const char *descr, + thread_func_t *func, int priority ) +{ + ts->task_threads[thr_idx] = hb_thread_init( descr, func, + taskset_thread_args( ts, thr_idx ), + priority); + return( ts->task_threads[thr_idx] != NULL ); +} + +void +taskset_cycle( taskset_t *ts ) +{ + hb_lock( ts->task_cond_lock ); + + /* + * Signal all threads that their work is available. + */ + bit_nset( ts->task_begin_bitmap, 0, ts->thread_count - 1 ); + hb_cond_broadcast( ts->task_begin ); + + /* + * Wait until all threads have completed. Note that we must + * loop here as hb_cond_wait() on some platforms (e.g pthead_cond_wait) + * may unblock prematurely. + */ + do + { + hb_cond_wait( ts->task_complete, ts->task_cond_lock ); + } while ( !allbits_set( ts->task_complete_bitmap, ts->bitmap_elements ) ); + + /* + * Clear completion indications for next time. + */ + bit_nclear( ts->task_complete_bitmap, 0, ts->thread_count - 1 ); + + hb_unlock( ts->task_cond_lock ); +} + +/* + * Block current thread until work is available for it. + */ +void +taskset_thread_wait4start( taskset_t *ts, int thr_idx ) +{ + hb_lock( ts->task_cond_lock ); + while ( bit_is_clear( ts->task_begin_bitmap, thr_idx ) ) + hb_cond_wait( ts->task_begin, ts->task_cond_lock ); + + /* + * We've been released for one run. Insure we block the next + * time through the loop. + */ + bit_clear( ts->task_begin_bitmap, thr_idx ); + hb_unlock( ts->task_cond_lock ); +} + +/* + * Current thread has completed its work. Indicate completion, + * and if all threads in this task set have completed, wakeup + * anyone waiting for this condition. + */ +void +taskset_thread_complete( taskset_t *ts, int thr_idx ) +{ + hb_lock( ts->task_cond_lock ); + bit_set( ts->task_complete_bitmap, thr_idx ); + if( allbits_set( ts->task_complete_bitmap, ts->bitmap_elements ) ) + { + hb_cond_signal( ts->task_complete ); + } + hb_unlock( ts->task_cond_lock ); +} + +void +taskset_fini( taskset_t *ts ) +{ + int i; + + hb_lock( ts->task_cond_lock ); + /* + * Tell each thread to stop, and then cleanup. + */ + bit_nset( ts->task_stop_bitmap, 0, ts->thread_count - 1 ); + bit_nset( ts->task_begin_bitmap, 0, ts->thread_count - 1 ); + hb_cond_broadcast( ts->task_begin ); + + /* + * Wait for all threads to exit. + */ + hb_cond_wait( ts->task_complete, ts->task_cond_lock ); + hb_unlock( ts->task_cond_lock ); + + /* + * Clean up taskset memory. + */ + for( i = 0; i < ts->thread_count; i++) + { + hb_thread_close( &ts->task_threads[i] ); + } + hb_lock_close( &ts->task_cond_lock ); + hb_cond_close( &ts->task_begin ); + hb_cond_close( &ts->task_complete ); + free( ts->task_threads ); + if( ts->task_threads_args != NULL ) + free( ts->task_threads_args ); + free( ts->task_begin_bitmap ); + free( ts->task_complete_bitmap ); + free( ts->task_stop_bitmap ); +} Index: libhb/deinterlace.c =================================================================== --- libhb/deinterlace.c (revision 3169) +++ libhb/deinterlace.c (working copy) @@ -19,6 +19,7 @@ #include "hb.h" #include "hbffmpeg.h" #include "mpeg2dec/mpeg2.h" +#include "taskset.h" #define SUPPRESS_AV_LOG @@ -36,7 +37,6 @@ uint8_t **dst; int parity; int tff; - int stop; } yadif_arguments_t; struct hb_filter_private_s @@ -54,9 +54,8 @@ int cpu_count; - hb_thread_t ** yadif_threads; // Threads for Yadif - one per CPU - hb_lock_t ** yadif_begin_lock; // Thread has work - hb_lock_t ** yadif_complete_lock; // Thread has completed work + taskset_t yadif_taskset; // Threads for Yadif - one per CPU + yadif_arguments_t *yadif_arguments; // Arguments to thread for work int mcdeint_mode; @@ -227,27 +226,27 @@ while( run ) { /* - * Wait here until there is work to do. hb_lock() blocks until - * render releases it to say that there is more work to do. + * Wait here until there is work to do. */ - hb_lock( pv->yadif_begin_lock[segment] ); + taskset_thread_wait4start( &pv->yadif_taskset, segment ); - yadif_work = &pv->yadif_arguments[segment]; - if( yadif_work->stop ) + if( taskset_thread_stop( &pv->yadif_taskset, segment ) ) { /* * No more work to do, exit this thread. */ run = 0; - continue; + goto report_completion; } + yadif_work = &pv->yadif_arguments[segment]; + if( yadif_work->dst == NULL ) { hb_error( "Thread started when no work available" ); hb_snooze(500); - continue; + goto report_completion; } /* @@ -339,12 +338,13 @@ } } } + +report_completion: /* * Finished this segment, let everyone know. */ - hb_unlock( pv->yadif_complete_lock[segment] ); + taskset_thread_complete( &pv->yadif_taskset, segment ); } - free( thread_args_v ); } @@ -371,28 +371,10 @@ pv->yadif_arguments[segment].parity = parity; pv->yadif_arguments[segment].tff = tff; pv->yadif_arguments[segment].dst = dst; - - /* - * Let the thread for this plane know that we've setup work - * for it by releasing the begin lock (ensuring that the - * complete lock is already locked so that we block when - * we try to lock it again below). - */ - hb_lock( pv->yadif_complete_lock[segment] ); - hb_unlock( pv->yadif_begin_lock[segment] ); } - /* - * Wait until all three threads have completed by trying to get - * the complete lock that we locked earlier for each thread, which - * will block until that thread has completed the work on that - * plane. - */ - for( segment = 0; segment < pv->cpu_count; segment++ ) - { - hb_lock( pv->yadif_complete_lock[segment] ); - hb_unlock( pv->yadif_complete_lock[segment] ); - } + /* Allow the taskset threads to make one pass over the data. */ + taskset_cycle( &pv->yadif_taskset ); /* * Entire frame is now deinterlaced. @@ -572,41 +554,32 @@ } /* - * Create yadif threads and locks. + * Setup yadif taskset. */ - pv->yadif_threads = malloc( sizeof( hb_thread_t* ) * pv->cpu_count ); - pv->yadif_begin_lock = malloc( sizeof( hb_lock_t * ) * pv->cpu_count ); - pv->yadif_complete_lock = malloc( sizeof( hb_lock_t * ) * pv->cpu_count ); pv->yadif_arguments = malloc( sizeof( yadif_arguments_t ) * pv->cpu_count ); + if( pv->yadif_arguments == NULL || + taskset_init( &pv->yadif_taskset, /*thread_count*/pv->cpu_count, + sizeof( yadif_arguments_t ) ) == 0 ) + { + hb_error( "yadif could not initialize taskset" ); + } for( i = 0; i < pv->cpu_count; i++ ) { yadif_thread_arg_t *thread_args; - thread_args = malloc( sizeof( yadif_thread_arg_t ) ); + thread_args = taskset_thread_args( &pv->yadif_taskset, i ); - if( thread_args ) { - thread_args->pv = pv; - thread_args->segment = i; + thread_args->pv = pv; + thread_args->segment = i; + pv->yadif_arguments[i].dst = NULL; - pv->yadif_begin_lock[i] = hb_lock_init(); - pv->yadif_complete_lock[i] = hb_lock_init(); - - /* - * Important to start off with the threads locked waiting - * on input. - */ - hb_lock( pv->yadif_begin_lock[i] ); - - pv->yadif_arguments[i].stop = 0; - pv->yadif_arguments[i].dst = NULL; - - pv->yadif_threads[i] = hb_thread_init( "yadif_filter_segment", - yadif_filter_thread, - thread_args, - HB_NORMAL_PRIORITY ); - } else { - hb_error( "Yadif could not create threads" ); + if( taskset_thread_spawn( &pv->yadif_taskset, i, + "yadif_filter_segment", + yadif_filter_thread, + HB_NORMAL_PRIORITY ) == 0 ) + { + hb_error( "yadif could not spawn thread" ); } } } @@ -699,25 +672,7 @@ } } - for( i = 0; i < pv->cpu_count; i++) - { - /* - * Tell each yadif thread to stop, and then cleanup. - */ - pv->yadif_arguments[i].stop = 1; - hb_unlock( pv->yadif_begin_lock[i] ); - - hb_thread_close( &pv->yadif_threads[i] ); - hb_lock_close( &pv->yadif_begin_lock[i] ); - hb_lock_close( &pv->yadif_complete_lock[i] ); - } - - /* - * free memory for yadif structs - */ - free( pv->yadif_threads ); - free( pv->yadif_begin_lock ); - free( pv->yadif_complete_lock ); + taskset_fini( &pv->yadif_taskset ); free( pv->yadif_arguments ); } Index: libhb/taskset.h =================================================================== --- libhb/taskset.h (revision 0) +++ libhb/taskset.h (revision 0) @@ -0,0 +1,52 @@ +/* $Id$ + + This file is part of the HandBrake source code. + Homepage: . + It may be used under the terms of the GNU General Public License. */ + +#ifndef HB_TASKSET_H +#define HB_TASKSET_H + +#define TASKSET_POSIX_COMPLIANT 1 + +#include "bits.h" + +typedef struct hb_taskset_s { + int thread_count; + int arg_size; + int bitmap_elements; + hb_thread_t ** task_threads; + uint8_t * task_threads_args; + uint32_t * task_begin_bitmap; // Threads can begin + uint32_t * task_complete_bitmap; // Threads have completed + uint32_t * task_stop_bitmap; // Threads should exit + hb_lock_t * task_cond_lock; // Held during condition tests + hb_cond_t * task_begin; // Threads can begin work + hb_cond_t * task_complete; // Threads have finished work. +} taskset_t; + +int taskset_init( taskset_t *, int /*thread_count*/, size_t /*user_arg_size*/ ); +void taskset_cycle( taskset_t * ); +void taskset_fini( taskset_t * ); + +int taskset_thread_spawn( taskset_t *, int /*thr_idx*/, const char * /*descr*/, + thread_func_t *, int /*priority*/ ); +void taskset_thread_wait4start( taskset_t *, int ); +void taskset_thread_complete( taskset_t *, int ); + +static inline void *taskset_thread_args( taskset_t *, int ); +static inline int taskset_thread_stop( taskset_t *, int ); + +static inline void * +taskset_thread_args( taskset_t *ts, int thr_idx ) +{ + return( ts->task_threads_args + ( ts->arg_size * thr_idx ) ); +} + +static inline int +taskset_thread_stop( taskset_t *ts, int thr_idx ) +{ + return bit_is_set( ts->task_stop_bitmap, thr_idx ); +} + +#endif /* HB_TASKSET_H */ Index: libhb/decomb.c =================================================================== --- libhb/decomb.c (revision 3169) +++ libhb/decomb.c (working copy) @@ -69,6 +69,7 @@ #include "hbffmpeg.h" #include "mpeg2dec/mpeg2.h" #include "eedi2.h" +#include "taskset.h" #define SUPPRESS_AV_LOG @@ -97,21 +98,10 @@ uint8_t **dst; int parity; int tff; - int stop; int is_combed; }; -struct decomb_arguments_s { - int stop; -}; - -struct eedi2_arguments_s { - int stop; -}; - typedef struct yadif_arguments_s yadif_arguments_t; -typedef struct decomb_arguments_s decomb_arguments_t; -typedef struct eedi2_arguments_s eedi2_arguments_t; typedef struct eedi2_thread_arg_s { hb_filter_private_t *pv; @@ -191,21 +181,13 @@ int cpu_count; - hb_thread_t ** yadif_threads; // Threads for Yadif - one per CPU - hb_lock_t ** yadif_begin_lock; // Thread has work - hb_lock_t ** yadif_complete_lock; // Thread has completed work - yadif_arguments_t *yadif_arguments; // Arguments to thread for work - - hb_thread_t ** decomb_threads; // Threads for comb detection - one per CPU - hb_lock_t ** decomb_begin_lock; // Thread has work - hb_lock_t ** decomb_complete_lock; // Thread has completed work - decomb_arguments_t *decomb_arguments; // Arguments to thread for work + taskset_t yadif_taskset; // Threads for Yadif - one per CPU - hb_thread_t ** eedi2_threads; // Threads for eedi2 - one per plane - hb_lock_t ** eedi2_begin_lock; // Thread has work - hb_lock_t ** eedi2_complete_lock; // Thread has completed work - eedi2_arguments_t *eedi2_arguments; // Arguments to thread for work + yadif_arguments_t *yadif_arguments; // Arguments to thread for work + taskset_t decomb_taskset; // Threads for comb detection - one per CPU + + taskset_t eedi2_taskset; // Threads for eedi2 - one per plane // int alternator; // for bobbing parity when framedoubling }; @@ -814,7 +796,6 @@ */ void eedi2_filter_thread( void *thread_args_v ) { - eedi2_arguments_t *eedi2_work = NULL; hb_filter_private_t * pv; int run = 1; int plane; @@ -828,31 +809,30 @@ while( run ) { /* - * Wait here until there is work to do. hb_lock() blocks until - * render releases it to say that there is more work to do. + * Wait here until there is work to do. */ - hb_lock( pv->eedi2_begin_lock[plane] ); + taskset_thread_wait4start( &pv->eedi2_taskset, plane ); - eedi2_work = &pv->eedi2_arguments[plane]; - - if( eedi2_work->stop ) + if( taskset_thread_stop( &pv->eedi2_taskset, plane ) ) { /* * No more work to do, exit this thread. */ run = 0; - continue; - } + } + else + { - /* - * Process plane - */ + /* + * Process plane + */ eedi2_interpolate_plane( pv, plane ); + } /* * Finished this segment, let everyone know. */ - hb_unlock( pv->eedi2_complete_lock[plane] ); + taskset_thread_complete( &pv->eedi2_taskset, plane ); } free( thread_args_v ); } @@ -870,30 +850,11 @@ eedi2_fill_half_height_buffer_plane( &pv->ref[1][i][pitch*start_line], pv->eedi_half[SRCPF][i], pitch, pv->height[i] ); } - int plane; - for( plane = 0; plane < 3; plane++ ) - { - /* - * Let the thread for this plane know that we've setup work - * for it by releasing the begin lock (ensuring that the - * complete lock is already locked so that we block when - * we try to lock it again below). - */ - hb_lock( pv->eedi2_complete_lock[plane] ); - hb_unlock( pv->eedi2_begin_lock[plane] ); - } - /* - * Wait until all three threads have completed by trying to get - * the complete lock that we locked earlier for each thread, which - * will block until that thread has completed the work on that - * plane. + * Now that all data is ready for our threads, fire them off + * and wait for their completion. */ - for( plane = 0; plane < 3; plane++ ) - { - hb_lock( pv->eedi2_complete_lock[plane] ); - hb_unlock( pv->eedi2_complete_lock[plane] ); - } + taskset_cycle( &pv->eedi2_taskset ); } @@ -902,7 +863,6 @@ */ void decomb_filter_thread( void *thread_args_v ) { - decomb_arguments_t *decomb_work = NULL; hb_filter_private_t * pv; int run = 1; int segment, segment_start, segment_stop, plane; @@ -916,20 +876,17 @@ while( run ) { /* - * Wait here until there is work to do. hb_lock() blocks until - * render releases it to say that there is more work to do. + * Wait here until there is work to do. */ - hb_lock( pv->decomb_begin_lock[segment] ); + taskset_thread_wait4start( &pv->decomb_taskset, segment ); - decomb_work = &pv->decomb_arguments[segment]; - - if( decomb_work->stop ) + if( taskset_thread_stop( &pv->decomb_taskset, segment ) ) { /* * No more work to do, exit this thread. */ run = 0; - continue; + goto report_completion; } /* @@ -952,41 +909,22 @@ detect_combed_segment( pv, segment_start, segment_stop ); } + +report_completion: /* * Finished this segment, let everyone know. */ - hb_unlock( pv->decomb_complete_lock[segment] ); + taskset_thread_complete( &pv->decomb_taskset, segment ); } - free( thread_args_v ); } int comb_segmenter( hb_filter_private_t * pv ) { - int segment; - - for( segment = 0; segment < pv->cpu_count; segment++ ) - { - /* - * Let the thread for this plane know that we've setup work - * for it by releasing the begin lock (ensuring that the - * complete lock is already locked so that we block when - * we try to lock it again below). - */ - hb_lock( pv->decomb_complete_lock[segment] ); - hb_unlock( pv->decomb_begin_lock[segment] ); - } - /* - * Wait until all three threads have completed by trying to get - * the complete lock that we locked earlier for each thread, which - * will block until that thread has completed the work on that - * plane. + * Now that all data for decomb detection is ready for + * our threads, fire them off and wait for their completion. */ - for( segment = 0; segment < pv->cpu_count; segment++ ) - { - hb_lock( pv->decomb_complete_lock[segment] ); - hb_unlock( pv->decomb_complete_lock[segment] ); - } + taskset_cycle( &pv->decomb_taskset ); return check_combing_mask( pv ); } @@ -1168,27 +1106,26 @@ while( run ) { /* - * Wait here until there is work to do. hb_lock() blocks until - * render releases it to say that there is more work to do. + * Wait here until there is work to do. */ - hb_lock( pv->yadif_begin_lock[segment] ); - - yadif_work = &pv->yadif_arguments[segment]; - - if( yadif_work->stop ) + taskset_thread_wait4start( &pv->yadif_taskset, segment ); + + if( taskset_thread_stop( &pv->yadif_taskset, segment ) ) { /* * No more work to do, exit this thread. */ run = 0; - continue; + goto report_completion; } + yadif_work = &pv->yadif_arguments[segment]; + if( yadif_work->dst == NULL ) { hb_error( "thread started when no work available" ); hb_snooze(500); - continue; + goto report_completion; } is_combed = pv->yadif_arguments[segment].is_combed; @@ -1301,12 +1238,13 @@ } } } + +report_completion: /* * Finished this segment, let everyone know. */ - hb_unlock( pv->yadif_complete_lock[segment] ); + taskset_thread_complete( &pv->yadif_taskset, segment ); } - free( thread_args_v ); } static void yadif_filter( uint8_t ** dst, @@ -1402,28 +1340,12 @@ pv->yadif_arguments[segment].tff = tff; pv->yadif_arguments[segment].dst = dst; pv->yadif_arguments[segment].is_combed = is_combed; - - /* - * Let the thread for this plane know that we've setup work - * for it by releasing the begin lock (ensuring that the - * complete lock is already locked so that we block when - * we try to lock it again below). - */ - hb_lock( pv->yadif_complete_lock[segment] ); - hb_unlock( pv->yadif_begin_lock[segment] ); } /* - * Wait until all three threads have completed by trying to get - * the complete lock that we locked earlier for each thread, which - * will block until that thread has completed the work on that - * plane. + * Allow the taskset threads to make one pass over the data. */ - for( segment = 0; segment < pv->cpu_count; segment++ ) - { - hb_lock( pv->yadif_complete_lock[segment] ); - hb_unlock( pv->yadif_complete_lock[segment] ); - } + taskset_cycle( &pv->yadif_taskset ); /* * Entire frame is now deinterlaced. @@ -1724,98 +1646,70 @@ } } - /* - * Create yadif threads and locks. - */ - pv->yadif_threads = malloc( sizeof( hb_thread_t* ) * pv->cpu_count ); - pv->yadif_begin_lock = malloc( sizeof( hb_lock_t * ) * pv->cpu_count ); - pv->yadif_complete_lock = malloc( sizeof( hb_lock_t * ) * pv->cpu_count ); - pv->yadif_arguments = malloc( sizeof( yadif_arguments_t ) * pv->cpu_count ); + /* + * Setup yadif taskset. + */ + pv->yadif_arguments = malloc( sizeof( yadif_arguments_t ) * pv->cpu_count ); + if( pv->yadif_arguments == NULL || + taskset_init( &pv->yadif_taskset, /*thread_count*/pv->cpu_count, + sizeof( yadif_thread_arg_t ) ) == 0 ) + { + hb_error( "yadif could not initialize taskset" ); + } - for( i = 0; i < pv->cpu_count; i++ ) - { - yadif_thread_arg_t *thread_args; + for( i = 0; i < pv->cpu_count; i++ ) + { + yadif_thread_arg_t *thread_args; - thread_args = malloc( sizeof( yadif_thread_arg_t ) ); - - if( thread_args ) - { - thread_args->pv = pv; - thread_args->segment = i; - - pv->yadif_begin_lock[i] = hb_lock_init(); - pv->yadif_complete_lock[i] = hb_lock_init(); - - /* - * Important to start off with the threads locked waiting - * on input. - */ - hb_lock( pv->yadif_begin_lock[i] ); - - pv->yadif_arguments[i].stop = 0; - pv->yadif_arguments[i].dst = NULL; - - pv->yadif_threads[i] = hb_thread_init( "yadif_filter_segment", - yadif_decomb_filter_thread, - thread_args, - HB_NORMAL_PRIORITY ); - } - else - { - hb_error( "yadif could not create threads" ); - } + thread_args = taskset_thread_args( &pv->yadif_taskset, i ); + thread_args->pv = pv; + thread_args->segment = i; + pv->yadif_arguments[i].dst = NULL; + if( taskset_thread_spawn( &pv->yadif_taskset, i, + "yadif_filter_segment", + yadif_decomb_filter_thread, + HB_NORMAL_PRIORITY ) == 0 ) + { + hb_error( "yadif could not spawn thread" ); + } } /* - * Create decomb threads and locks. + * Create decomb taskset. */ - pv->decomb_threads = malloc( sizeof( hb_thread_t* ) * pv->cpu_count ); - pv->decomb_begin_lock = malloc( sizeof( hb_lock_t * ) * pv->cpu_count ); - pv->decomb_complete_lock = malloc( sizeof( hb_lock_t * ) * pv->cpu_count ); - pv->decomb_arguments = malloc( sizeof( decomb_arguments_t ) * pv->cpu_count ); - + if( taskset_init( &pv->decomb_taskset, /*thread_count*/pv->cpu_count, + sizeof( decomb_thread_arg_t ) ) == 0 ) + { + hb_error( "decomb could not initialize taskset" ); + } for( i = 0; i < pv->cpu_count; i++ ) { decomb_thread_arg_t *decomb_thread_args; - decomb_thread_args = malloc( sizeof( decomb_thread_arg_t ) ); - - if( decomb_thread_args ) + decomb_thread_args = taskset_thread_args( &pv->decomb_taskset, i ); + decomb_thread_args->pv = pv; + decomb_thread_args->segment = i; + + if( taskset_thread_spawn( &pv->decomb_taskset, i, + "decomb_filter_segment", + decomb_filter_thread, + HB_NORMAL_PRIORITY ) == 0 ) { - decomb_thread_args->pv = pv; - decomb_thread_args->segment = i; - - pv->decomb_begin_lock[i] = hb_lock_init(); - pv->decomb_complete_lock[i] = hb_lock_init(); - - /* - * Important to start off with the threads locked waiting - * on input. - */ - hb_lock( pv->decomb_begin_lock[i] ); - - pv->decomb_arguments[i].stop = 0; - - pv->decomb_threads[i] = hb_thread_init( "decomb_filter_segment", - decomb_filter_thread, - decomb_thread_args, - HB_NORMAL_PRIORITY ); + hb_error( "decomb could not spawn thread" ); } - else - { - hb_error( "decomb could not create threads" ); - } } if( pv->mode & MODE_EEDI2 ) { + /* - * Create eedi2 threads and locks. + * Create eedi2 taskset. */ - pv->eedi2_threads = malloc( sizeof( hb_thread_t* ) * 3 ); - pv->eedi2_begin_lock = malloc( sizeof( hb_lock_t * ) * 3 ); - pv->eedi2_complete_lock = malloc( sizeof( hb_lock_t * ) * 3 ); - pv->eedi2_arguments = malloc( sizeof( eedi2_arguments_t ) * 3 ); + if( taskset_init( &pv->eedi2_taskset, /*thread_count*/3, + sizeof( eedi2_thread_arg_t ) ) == 0 ) + { + hb_error( "eedi2 could not initialize taskset" ); + } if( pv->post_processing > 1 ) { @@ -1833,37 +1727,21 @@ { eedi2_thread_arg_t *eedi2_thread_args; - eedi2_thread_args = malloc( sizeof( eedi2_thread_arg_t ) ); + eedi2_thread_args = taskset_thread_args( &pv->eedi2_taskset, i ); - if( eedi2_thread_args ) - { - eedi2_thread_args->pv = pv; - eedi2_thread_args->plane = i; + eedi2_thread_args->pv = pv; + eedi2_thread_args->plane = i; - pv->eedi2_begin_lock[i] = hb_lock_init(); - pv->eedi2_complete_lock[i] = hb_lock_init(); - - /* - * Important to start off with the threads locked waiting - * on input. - */ - hb_lock( pv->eedi2_begin_lock[i] ); - - pv->eedi2_arguments[i].stop = 0; - - pv->eedi2_threads[i] = hb_thread_init( "eedi2_filter_segment", - eedi2_filter_thread, - eedi2_thread_args, - HB_NORMAL_PRIORITY ); - } - else + if( taskset_thread_spawn( &pv->eedi2_taskset, i, + "eedi2_filter_segment", + eedi2_filter_thread, + HB_NORMAL_PRIORITY ) == 0 ) { - hb_error( "eedi2 could not create threads" ); + hb_error( "eedi2 could not spawn thread" ); } } } - /* Allocate mcdeint specific buffers */ if( pv->mcdeint_mode >= 0 ) { @@ -2001,70 +1879,17 @@ if (pv->tmpc) eedi2_aligned_free(pv->tmpc); } - for( i = 0; i < pv->cpu_count; i++) - { - /* - * Tell each yadif thread to stop, and then cleanup. - */ - pv->yadif_arguments[i].stop = 1; - hb_unlock( pv->yadif_begin_lock[i] ); - - hb_thread_close( &pv->yadif_threads[i] ); - hb_lock_close( &pv->yadif_begin_lock[i] ); - hb_lock_close( &pv->yadif_complete_lock[i] ); - } + taskset_fini( &pv->yadif_taskset ); + taskset_fini( &pv->decomb_taskset ); /* * free memory for yadif structs */ - free( pv->yadif_threads ); - free( pv->yadif_begin_lock ); - free( pv->yadif_complete_lock ); free( pv->yadif_arguments ); - for( i = 0; i < pv->cpu_count; i++) - { - /* - * Tell each decomb thread to stop, and then cleanup. - */ - pv->decomb_arguments[i].stop = 1; - hb_unlock( pv->decomb_begin_lock[i] ); - - hb_thread_close( &pv->decomb_threads[i] ); - hb_lock_close( &pv->decomb_begin_lock[i] ); - hb_lock_close( &pv->decomb_complete_lock[i] ); - } - - /* - * free memory for decomb structs - */ - free( pv->decomb_threads ); - free( pv->decomb_begin_lock ); - free( pv->decomb_complete_lock ); - free( pv->decomb_arguments ); - if( pv->mode & MODE_EEDI2 ) { - for( i = 0; i < 3; i++) - { - /* - * Tell each eedi2 thread to stop, and then cleanup. - */ - pv->eedi2_arguments[i].stop = 1; - hb_unlock( pv->eedi2_begin_lock[i] ); - - hb_thread_close( &pv->eedi2_threads[i] ); - hb_lock_close( &pv->eedi2_begin_lock[i] ); - hb_lock_close( &pv->eedi2_complete_lock[i] ); - } - - /* - * free memory for eedi2 structs - */ - free( pv->eedi2_threads ); - free( pv->eedi2_begin_lock ); - free( pv->eedi2_complete_lock ); - free( pv->eedi2_arguments ); + taskset_fini( &pv->eedi2_taskset ); } /* Cleanup mcdeint specific buffers */