All pastes #1852546 Raw Edit

HandBrake POSIX Taskset Take 2

public text v1 · immutable
#1852546 ·published 2010-03-26 02:57 UTC
rendered paste body
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: <http://handbrake.fr/>.
+   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( &param, 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: <http://handbrake.fr/>.
+   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: <http://handbrake.fr/>.
+   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 */