mirror of
				https://github.com/nyanmisaka/ffmpeg-rockchip.git
				synced 2025-11-01 04:53:04 +08:00 
			
		
		
		
	 a3d3cfa65c
			
		
	
	a3d3cfa65c
	
	
	
		
			
			Rework it to improve performance. Now mutex is not shared by workers, instead each worker has its own mutex and condition variable. This reduces lock contention between workers. Also use atomic variable for counter. The interface also allows execute to run special function on main thread, requested by Ronald. Signed-off-by: Muhammad Faiz <mfcc64@gmail.com>
		
			
				
	
	
		
			260 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			260 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * This file is part of FFmpeg.
 | |
|  *
 | |
|  * FFmpeg is free software; you can redistribute it and/or
 | |
|  * modify it under the terms of the GNU Lesser General Public
 | |
|  * License as published by the Free Software Foundation; either
 | |
|  * version 2.1 of the License, or (at your option) any later version.
 | |
|  *
 | |
|  * FFmpeg is distributed in the hope that it will be useful,
 | |
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
|  * Lesser General Public License for more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU Lesser General Public
 | |
|  * License along with FFmpeg; if not, write to the Free Software
 | |
|  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 | |
|  */
 | |
| 
 | |
| #include <stdatomic.h>
 | |
| #include "slicethread.h"
 | |
| #include "mem.h"
 | |
| #include "thread.h"
 | |
| #include "avassert.h"
 | |
| 
 | |
| #if HAVE_PTHREADS || HAVE_W32THREADS || HAVE_OS2THREADS
 | |
| 
 | |
| typedef struct WorkerContext {
 | |
|     AVSliceThread   *ctx;
 | |
|     pthread_mutex_t mutex;
 | |
|     pthread_cond_t  cond;
 | |
|     pthread_t       thread;
 | |
|     int             done;
 | |
| } WorkerContext;
 | |
| 
 | |
| struct AVSliceThread {
 | |
|     WorkerContext   *workers;
 | |
|     int             nb_threads;
 | |
|     int             nb_active_threads;
 | |
|     int             nb_jobs;
 | |
| 
 | |
|     atomic_uint     first_job;
 | |
|     atomic_uint     current_job;
 | |
|     pthread_mutex_t done_mutex;
 | |
|     pthread_cond_t  done_cond;
 | |
|     int             done;
 | |
|     int             finished;
 | |
| 
 | |
|     void            *priv;
 | |
|     void            (*worker_func)(void *priv, int jobnr, int threadnr, int nb_jobs, int nb_threads);
 | |
|     void            (*main_func)(void *priv);
 | |
| };
 | |
| 
 | |
| static int run_jobs(AVSliceThread *ctx)
 | |
| {
 | |
|     unsigned nb_jobs    = ctx->nb_jobs;
 | |
|     unsigned nb_active_threads = ctx->nb_active_threads;
 | |
|     unsigned first_job    = atomic_fetch_add_explicit(&ctx->first_job, 1, memory_order_acq_rel);
 | |
|     unsigned current_job  = first_job;
 | |
| 
 | |
|     do {
 | |
|         ctx->worker_func(ctx->priv, current_job, first_job, nb_jobs, nb_active_threads);
 | |
|     } while ((current_job = atomic_fetch_add_explicit(&ctx->current_job, 1, memory_order_acq_rel)) < nb_jobs);
 | |
| 
 | |
|     return current_job == nb_jobs + nb_active_threads - 1;
 | |
| }
 | |
| 
 | |
| static void *attribute_align_arg thread_worker(void *v)
 | |
| {
 | |
|     WorkerContext *w = v;
 | |
|     AVSliceThread *ctx = w->ctx;
 | |
| 
 | |
|     pthread_mutex_lock(&w->mutex);
 | |
|     pthread_cond_signal(&w->cond);
 | |
| 
 | |
|     while (1) {
 | |
|         w->done = 1;
 | |
|         while (w->done)
 | |
|             pthread_cond_wait(&w->cond, &w->mutex);
 | |
| 
 | |
|         if (ctx->finished) {
 | |
|             pthread_mutex_unlock(&w->mutex);
 | |
|             return NULL;
 | |
|         }
 | |
| 
 | |
|         if (run_jobs(ctx)) {
 | |
|             pthread_mutex_lock(&ctx->done_mutex);
 | |
|             ctx->done = 1;
 | |
|             pthread_cond_signal(&ctx->done_cond);
 | |
|             pthread_mutex_unlock(&ctx->done_mutex);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| int avpriv_slicethread_create(AVSliceThread **pctx, void *priv,
 | |
|                               void (*worker_func)(void *priv, int jobnr, int threadnr, int nb_jobs, int nb_threads),
 | |
|                               void (*main_func)(void *priv),
 | |
|                               int nb_threads)
 | |
| {
 | |
|     AVSliceThread *ctx;
 | |
|     int nb_workers, i;
 | |
| 
 | |
| #if HAVE_W32THREADS
 | |
|     w32thread_init();
 | |
| #endif
 | |
| 
 | |
|     av_assert0(nb_threads >= 0);
 | |
|     if (!nb_threads) {
 | |
|         int nb_cpus = av_cpu_count();
 | |
|         if (nb_cpus > 1)
 | |
|             nb_threads = nb_cpus + 1;
 | |
|         else
 | |
|             nb_threads = 1;
 | |
|     }
 | |
| 
 | |
|     nb_workers = nb_threads;
 | |
|     if (!main_func)
 | |
|         nb_workers--;
 | |
| 
 | |
|     *pctx = ctx = av_mallocz(sizeof(*ctx));
 | |
|     if (!ctx)
 | |
|         return AVERROR(ENOMEM);
 | |
| 
 | |
|     if (nb_workers && !(ctx->workers = av_calloc(nb_workers, sizeof(*ctx->workers)))) {
 | |
|         av_freep(pctx);
 | |
|         return AVERROR(ENOMEM);
 | |
|     }
 | |
| 
 | |
|     ctx->priv        = priv;
 | |
|     ctx->worker_func = worker_func;
 | |
|     ctx->main_func   = main_func;
 | |
|     ctx->nb_threads  = nb_threads;
 | |
|     ctx->nb_active_threads = 0;
 | |
|     ctx->nb_jobs     = 0;
 | |
|     ctx->finished    = 0;
 | |
| 
 | |
|     atomic_init(&ctx->first_job, 0);
 | |
|     atomic_init(&ctx->current_job, 0);
 | |
|     pthread_mutex_init(&ctx->done_mutex, NULL);
 | |
|     pthread_cond_init(&ctx->done_cond, NULL);
 | |
|     ctx->done        = 0;
 | |
| 
 | |
|     for (i = 0; i < nb_workers; i++) {
 | |
|         WorkerContext *w = &ctx->workers[i];
 | |
|         int ret;
 | |
|         w->ctx = ctx;
 | |
|         pthread_mutex_init(&w->mutex, NULL);
 | |
|         pthread_cond_init(&w->cond, NULL);
 | |
|         pthread_mutex_lock(&w->mutex);
 | |
|         w->done = 0;
 | |
| 
 | |
|         if (ret = pthread_create(&w->thread, NULL, thread_worker, w)) {
 | |
|             ctx->nb_threads = main_func ? i : i + 1;
 | |
|             pthread_mutex_unlock(&w->mutex);
 | |
|             pthread_cond_destroy(&w->cond);
 | |
|             pthread_mutex_destroy(&w->mutex);
 | |
|             avpriv_slicethread_free(pctx);
 | |
|             return AVERROR(ret);
 | |
|         }
 | |
| 
 | |
|         while (!w->done)
 | |
|             pthread_cond_wait(&w->cond, &w->mutex);
 | |
|         pthread_mutex_unlock(&w->mutex);
 | |
|     }
 | |
| 
 | |
|     return nb_threads;
 | |
| }
 | |
| 
 | |
| void avpriv_slicethread_execute(AVSliceThread *ctx, int nb_jobs, int execute_main)
 | |
| {
 | |
|     int nb_workers, i, is_last = 0;
 | |
| 
 | |
|     av_assert0(nb_jobs > 0);
 | |
|     ctx->nb_jobs           = nb_jobs;
 | |
|     ctx->nb_active_threads = FFMIN(nb_jobs, ctx->nb_threads);
 | |
|     atomic_store_explicit(&ctx->first_job, 0, memory_order_relaxed);
 | |
|     atomic_store_explicit(&ctx->current_job, ctx->nb_active_threads, memory_order_relaxed);
 | |
|     nb_workers             = ctx->nb_active_threads;
 | |
|     if (!ctx->main_func || !execute_main)
 | |
|         nb_workers--;
 | |
| 
 | |
|     for (i = 0; i < nb_workers; i++) {
 | |
|         WorkerContext *w = &ctx->workers[i];
 | |
|         pthread_mutex_lock(&w->mutex);
 | |
|         w->done = 0;
 | |
|         pthread_cond_signal(&w->cond);
 | |
|         pthread_mutex_unlock(&w->mutex);
 | |
|     }
 | |
| 
 | |
|     if (ctx->main_func && execute_main)
 | |
|         ctx->main_func(ctx->priv);
 | |
|     else
 | |
|         is_last = run_jobs(ctx);
 | |
| 
 | |
|     if (!is_last) {
 | |
|         pthread_mutex_lock(&ctx->done_mutex);
 | |
|         while (!ctx->done)
 | |
|             pthread_cond_wait(&ctx->done_cond, &ctx->done_mutex);
 | |
|         ctx->done = 0;
 | |
|         pthread_mutex_unlock(&ctx->done_mutex);
 | |
|     }
 | |
| }
 | |
| 
 | |
| void avpriv_slicethread_free(AVSliceThread **pctx)
 | |
| {
 | |
|     AVSliceThread *ctx;
 | |
|     int nb_workers, i;
 | |
| 
 | |
|     if (!pctx || !*pctx)
 | |
|         return;
 | |
| 
 | |
|     ctx = *pctx;
 | |
|     nb_workers = ctx->nb_threads;
 | |
|     if (!ctx->main_func)
 | |
|         nb_workers--;
 | |
| 
 | |
|     ctx->finished = 1;
 | |
|     for (i = 0; i < nb_workers; i++) {
 | |
|         WorkerContext *w = &ctx->workers[i];
 | |
|         pthread_mutex_lock(&w->mutex);
 | |
|         w->done = 0;
 | |
|         pthread_cond_signal(&w->cond);
 | |
|         pthread_mutex_unlock(&w->mutex);
 | |
|     }
 | |
| 
 | |
|     for (i = 0; i < nb_workers; i++) {
 | |
|         WorkerContext *w = &ctx->workers[i];
 | |
|         pthread_join(w->thread, NULL);
 | |
|         pthread_cond_destroy(&w->cond);
 | |
|         pthread_mutex_destroy(&w->mutex);
 | |
|     }
 | |
| 
 | |
|     pthread_cond_destroy(&ctx->done_cond);
 | |
|     pthread_mutex_destroy(&ctx->done_mutex);
 | |
|     av_freep(&ctx->workers);
 | |
|     av_freep(pctx);
 | |
| }
 | |
| 
 | |
| #else /* HAVE_PTHREADS || HAVE_W32THREADS || HAVE_OS32THREADS */
 | |
| 
 | |
| int avpriv_slicethread_create(AVSliceThread **pctx, void *priv,
 | |
|                               void (*worker_func)(void *priv, int jobnr, int threadnr, int nb_jobs, int nb_threads),
 | |
|                               void (*main_func)(void *priv),
 | |
|                               int nb_threads)
 | |
| {
 | |
|     *pctx = NULL;
 | |
|     return AVERROR(EINVAL);
 | |
| }
 | |
| 
 | |
| void avpriv_slicethread_execute(AVSliceThread *ctx, int nb_jobs, int execute_main)
 | |
| {
 | |
|     av_assert0(0);
 | |
| }
 | |
| 
 | |
| void avpriv_slicethread_free(AVSliceThread **pctx)
 | |
| {
 | |
|     av_assert0(!pctx || !*pctx);
 | |
| }
 | |
| 
 | |
| #endif /* HAVE_PTHREADS || HAVE_W32THREADS || HAVE_OS32THREADS */
 |