/* taskset.c Copyright (c) 2003-2025 HandBrake Team This file is part of the HandBrake source code Homepage: . It may be used under the terms of the GNU General Public License v2. For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html */ #include "handbrake/handbrake.h" #include "handbrake/ports.h" #include "handbrake/taskset.h" static void taskset_thread_f( void *thread_args_v ); int taskset_init( taskset_t *ts, const char *descr, int thread_count, size_t arg_size, thread_func_t *work_func) { int init_step, i; init_step = 0; memset( ts, 0, sizeof( *ts ) ); ts->work_func = work_func; ts->thread_count = thread_count; ts->task_descr = descr; ts->arg_size = arg_size; if( arg_size != 0 ) { ts->task_threads_args = malloc( arg_size * ts->thread_count ); if( ts->task_threads_args == NULL ) goto fail; } /* * Initialize all arg data to 0. */ memset(ts->task_threads_args, 0, ts->arg_size * ts->thread_count ); init_step++; ts->task_thread_started = 0; ts->task_threads = calloc( ts->thread_count, sizeof( taskset_thread_t) ); if( ts->task_threads == NULL ) goto fail; init_step++; for ( i = 0; i < ts->thread_count; i++ ) { taskset_thread_t *thread = &ts->task_threads[i]; thread->lock = hb_lock_init(); if ( thread->lock == NULL ) goto fail; thread->begin_cond = hb_cond_init(); if ( thread->begin_cond == NULL ) goto fail; thread->complete_cond = hb_cond_init(); if ( thread->complete_cond == NULL ) goto fail; } return (1); fail: switch (init_step) { default: case 3: for ( i = 0; i < ts->thread_count; i++ ) { taskset_thread_t *thread = &ts->task_threads[i]; if ( thread->begin_cond ) hb_cond_close( &thread->begin_cond ); if ( thread->complete_cond ) hb_cond_close( &thread->complete_cond ); if ( thread->lock ) hb_lock_close( &thread->lock ); } /* FALL THROUGH */ case 2: free( ts->task_threads ); /* FALL THROUGH */ case 1: free( ts->task_threads_args ); /* FALL THROUGH */ case 0: break; } return (0); } static taskset_thread_t* taskset_thread( taskset_t *ts, int thr_idx ) { return &ts->task_threads[thr_idx]; } void taskset_cycle( taskset_t *ts ) { int i; if ( !ts->task_thread_started ) { for ( i = 0; i < ts->thread_count; i++ ) { taskset_thread_t *thread = taskset_thread( ts, i ); thread->thread = hb_thread_init( ts->task_descr, taskset_thread_f, taskset_thread_args( ts, i ), HB_NORMAL_PRIORITY ); } ts->task_thread_started = 1; } /* * Signal all threads that their work is available. */ for (i = 0; i < ts->thread_count; i++) { taskset_thread_t *thread = taskset_thread( ts, i ); hb_lock( thread->lock ); thread->begin = 1; hb_cond_signal( thread->begin_cond ); hb_unlock( thread->lock ); } /* * Wait until all threads have completed. Note that we must * loop here as hb_cond_wait() on some platforms (e.g pthread_cond_wait) * may unblock prematurely. */ for ( i = 0; i < ts->thread_count; i++ ) { taskset_thread_t *thread = taskset_thread( ts, i ); hb_lock( thread->lock ); while (!thread->complete) { hb_cond_wait( thread->complete_cond, thread->lock); } thread->complete = 0; hb_unlock( thread->lock ); } } /* * Block current thread until work is available for it. */ static void taskset_thread_wait4start( taskset_thread_t *thread ) { hb_lock( thread->lock ); while ( !thread->begin ) { hb_cond_wait( thread->begin_cond, thread->lock ); } /* * We've been released for one run. Insure we block the next * time through the loop. */ thread->begin = 0; hb_unlock( thread->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. */ static void taskset_thread_complete( taskset_thread_t *thread ) { hb_lock( thread->lock ); thread->complete = 1; hb_cond_signal( thread->complete_cond ); hb_unlock( thread->lock ); } static void taskset_thread_f( void *thread_args_v ) { taskset_thread_arg_t *thread_args = thread_args_v; int segment = thread_args->segment; taskset_thread_t *thread = taskset_thread( thread_args->taskset, segment ); while (1) { /* * Wait here until there is work to do. */ taskset_thread_wait4start( thread ); if( thread->stop ) { /* * No more work to do, exit this thread. */ break; } thread_args->taskset->work_func( thread_args_v ); taskset_thread_complete( thread ); } /* * Finished this segment, let everyone know. */ taskset_thread_complete( thread ); } void taskset_fini( taskset_t *ts ) { if (ts == NULL) { return; } int i; if ( ts->task_thread_started ) { /* * Tell each thread to stop, and then cleanup. */ for ( i = 0; i < ts->thread_count; i++ ) { taskset_thread_t *thread = taskset_thread( ts, i ); hb_lock( thread->lock ); thread->begin = 1; thread->stop = 1; hb_cond_signal( thread->begin_cond ); while ( !thread->complete ) { hb_cond_wait( thread->complete_cond, thread->lock ); } hb_unlock( thread->lock ); } /* * Clean up thread memory. */ for( i = 0; i < ts->thread_count; i++ ) { taskset_thread_t *thread = taskset_thread( ts, i ); hb_thread_close( &thread->thread ); } } /* * Clean up taskset memory. */ for( i = 0; i < ts->thread_count; i++ ) { taskset_thread_t *thread = taskset_thread( ts, i ); hb_lock_close( &thread->lock ); hb_cond_close( &thread->begin_cond ); hb_cond_close( &thread->complete_cond ); } free( ts->task_threads ); if( ts->task_threads_args != NULL ) free( ts->task_threads_args ); }