FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavutil/slicethread.c
Date: 2026-08-29 16:27:19
Exec Total Coverage
Lines: 118 165 71.5%
Functions: 5 9 55.6%
Branches: 45 76 59.2%

Line Branch Exec Source
1 /*
2 * This file is part of FFmpeg.
3 *
4 * FFmpeg is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * FFmpeg is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with FFmpeg; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19 #include <stdatomic.h>
20 #include "cpu.h"
21 #include "internal.h"
22 #include "slicethread.h"
23 #include "mem.h"
24 #include "thread.h"
25 #include "avassert.h"
26
27 #define MAX_AUTO_THREADS 16
28
29 #if HAVE_PTHREADS || HAVE_W32THREADS || HAVE_OS2THREADS
30
31 typedef struct WorkerContext {
32 AVSliceThread *ctx;
33 pthread_mutex_t mutex;
34 pthread_cond_t cond;
35 pthread_t thread;
36 int done;
37 } WorkerContext;
38
39 struct AVSliceThread {
40 WorkerContext *workers;
41 int nb_threads;
42 int nb_active_threads;
43 int nb_jobs;
44
45 atomic_uint first_job;
46 atomic_uint current_job;
47 pthread_mutex_t done_mutex;
48 pthread_cond_t done_cond;
49 int done;
50 int finished;
51 atomic_int error;
52
53 void *priv;
54 int (*worker_func)(void *priv, int jobnr, int threadnr, int nb_jobs, int nb_threads);
55 int (*main_func)(void *priv);
56
57 #if LIBAVUTIL_VERSION_MAJOR < 62
58 void (*worker_func_v1)(void *priv, int jobnr, int threadnr, int nb_jobs, int nb_threads);
59 void (*main_func_v1)(void *priv);
60 void *priv_v1;
61 #endif
62 };
63
64 659950 static int run_jobs(AVSliceThread *ctx)
65 {
66 659950 unsigned nb_jobs = ctx->nb_jobs;
67 659950 unsigned nb_active_threads = ctx->nb_active_threads;
68 659950 unsigned first_job = atomic_fetch_add_explicit(&ctx->first_job, 1, memory_order_acq_rel);
69 659950 unsigned current_job = first_job;
70
71 do {
72 662038 int ret = atomic_load_explicit(&ctx->error, memory_order_relaxed);
73
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 662038 times.
662038 if (ret)
74 continue;
75 662038 ret = ctx->worker_func(ctx->priv, current_job, first_job, nb_jobs, nb_active_threads);
76
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 662038 times.
662038 if (ret) {
77 int prev = 0;
78 atomic_compare_exchange_strong_explicit(&ctx->error, &prev, ret,
79 memory_order_relaxed,
80 memory_order_relaxed);
81 }
82
2/2
✓ Branch 0 taken 2088 times.
✓ Branch 1 taken 659950 times.
662038 } while ((current_job = atomic_fetch_add_explicit(&ctx->current_job, 1, memory_order_acq_rel)) < nb_jobs);
83
84 659950 return current_job == nb_jobs + nb_active_threads - 1;
85 }
86
87 52329 static void *attribute_align_arg thread_worker(void *v)
88 {
89 52329 WorkerContext *w = v;
90 52329 AVSliceThread *ctx = w->ctx;
91
92 52329 pthread_mutex_lock(&w->mutex);
93 52329 pthread_cond_signal(&w->cond);
94
95 while (1) {
96 636891 w->done = 1;
97
2/2
✓ Branch 0 taken 636891 times.
✓ Branch 1 taken 636891 times.
1273782 while (w->done)
98 636891 pthread_cond_wait(&w->cond, &w->mutex);
99
100
2/2
✓ Branch 0 taken 52329 times.
✓ Branch 1 taken 584562 times.
636891 if (ctx->finished) {
101 52329 pthread_mutex_unlock(&w->mutex);
102 52329 return NULL;
103 }
104
105
2/2
✓ Branch 1 taken 70487 times.
✓ Branch 2 taken 514075 times.
584562 if (run_jobs(ctx)) {
106 70487 pthread_mutex_lock(&ctx->done_mutex);
107 70487 ctx->done = 1;
108 70487 pthread_cond_signal(&ctx->done_cond);
109 70487 pthread_mutex_unlock(&ctx->done_mutex);
110 }
111 }
112 }
113
114 av_cold
115 6579 int avpriv_slicethread_create2(AVSliceThread **pctx, void *priv,
116 int (*worker_func)(void *priv, int jobnr, int threadnr, int nb_jobs, int nb_threads),
117 int (*main_func)(void *priv),
118 int nb_threads)
119 {
120 AVSliceThread *ctx;
121 int nb_workers, i;
122 int ret;
123
124
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6579 times.
6579 av_assert0(nb_threads >= 0);
125
2/2
✓ Branch 0 taken 3928 times.
✓ Branch 1 taken 2651 times.
6579 if (!nb_threads) {
126 3928 int nb_cpus = av_cpu_count();
127
1/2
✓ Branch 0 taken 3928 times.
✗ Branch 1 not taken.
3928 if (nb_cpus > 1)
128 3928 nb_threads = FFMIN(nb_cpus + 1, MAX_AUTO_THREADS);
129 else
130 nb_threads = 1;
131 }
132
133 6579 nb_workers = nb_threads;
134
1/2
✓ Branch 0 taken 6579 times.
✗ Branch 1 not taken.
6579 if (!main_func)
135 6579 nb_workers--;
136
137 6579 *pctx = ctx = av_mallocz(sizeof(*ctx));
138
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6579 times.
6579 if (!ctx)
139 return AVERROR(ENOMEM);
140
141
3/4
✓ Branch 0 taken 6577 times.
✓ Branch 1 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6577 times.
6579 if (nb_workers && !(ctx->workers = av_calloc(nb_workers, sizeof(*ctx->workers)))) {
142 av_freep(pctx);
143 return AVERROR(ENOMEM);
144 }
145
146 6579 ctx->priv = priv;
147 6579 ctx->worker_func = worker_func;
148 6579 ctx->main_func = main_func;
149 6579 ctx->nb_threads = nb_threads;
150 6579 ctx->nb_active_threads = 0;
151 6579 ctx->nb_jobs = 0;
152 6579 ctx->finished = 0;
153
154 6579 atomic_init(&ctx->first_job, 0);
155 6579 atomic_init(&ctx->current_job, 0);
156 6579 ret = pthread_mutex_init(&ctx->done_mutex, NULL);
157
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6579 times.
6579 if (ret) {
158 av_freep(&ctx->workers);
159 av_freep(pctx);
160 return AVERROR(ret);
161 }
162 6579 ret = pthread_cond_init(&ctx->done_cond, NULL);
163
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6579 times.
6579 if (ret) {
164 ctx->nb_threads = main_func ? 0 : 1;
165 avpriv_slicethread_free(pctx);
166 return AVERROR(ret);
167 }
168 6579 ctx->done = 0;
169
170
2/2
✓ Branch 0 taken 52329 times.
✓ Branch 1 taken 6579 times.
58908 for (i = 0; i < nb_workers; i++) {
171 52329 WorkerContext *w = &ctx->workers[i];
172 52329 w->ctx = ctx;
173 52329 ret = pthread_mutex_init(&w->mutex, NULL);
174
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 52329 times.
52329 if (ret) {
175 ctx->nb_threads = main_func ? i : i + 1;
176 avpriv_slicethread_free(pctx);
177 return AVERROR(ret);
178 }
179 52329 ret = pthread_cond_init(&w->cond, NULL);
180
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 52329 times.
52329 if (ret) {
181 pthread_mutex_destroy(&w->mutex);
182 ctx->nb_threads = main_func ? i : i + 1;
183 avpriv_slicethread_free(pctx);
184 return AVERROR(ret);
185 }
186 52329 pthread_mutex_lock(&w->mutex);
187 52329 w->done = 0;
188
189
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 52329 times.
52329 if (ret = pthread_create(&w->thread, NULL, thread_worker, w)) {
190 ctx->nb_threads = main_func ? i : i + 1;
191 pthread_mutex_unlock(&w->mutex);
192 pthread_cond_destroy(&w->cond);
193 pthread_mutex_destroy(&w->mutex);
194 avpriv_slicethread_free(pctx);
195 return AVERROR(ret);
196 }
197
198
2/2
✓ Branch 0 taken 52329 times.
✓ Branch 1 taken 52329 times.
104658 while (!w->done)
199 52329 pthread_cond_wait(&w->cond, &w->mutex);
200 52329 pthread_mutex_unlock(&w->mutex);
201 }
202
203 6579 return nb_threads;
204 }
205
206 75388 int avpriv_slicethread_execute2(AVSliceThread *ctx, int nb_jobs, int execute_main)
207 {
208 75388 int nb_workers, i, is_last = 0, ret = 0;
209
210
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 75388 times.
75388 av_assert0(nb_jobs > 0);
211 75388 ctx->nb_jobs = nb_jobs;
212 75388 ctx->nb_active_threads = FFMIN(nb_jobs, ctx->nb_threads);
213 75388 atomic_store_explicit(&ctx->error, 0, memory_order_relaxed);
214 75388 atomic_store_explicit(&ctx->first_job, 0, memory_order_relaxed);
215 75388 atomic_store_explicit(&ctx->current_job, ctx->nb_active_threads, memory_order_relaxed);
216 75388 nb_workers = ctx->nb_active_threads;
217
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 75388 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
75388 if (!ctx->main_func || !execute_main)
218 75388 nb_workers--;
219
220
2/2
✓ Branch 0 taken 584562 times.
✓ Branch 1 taken 75388 times.
659950 for (i = 0; i < nb_workers; i++) {
221 584562 WorkerContext *w = &ctx->workers[i];
222 584562 pthread_mutex_lock(&w->mutex);
223 584562 w->done = 0;
224 584562 pthread_cond_signal(&w->cond);
225 584562 pthread_mutex_unlock(&w->mutex);
226 }
227
228
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 75388 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
75388 if (ctx->main_func && execute_main) {
229 ret = ctx->main_func(ctx->priv);
230 } else
231 75388 is_last = run_jobs(ctx);
232
233
2/2
✓ Branch 0 taken 70487 times.
✓ Branch 1 taken 4901 times.
75388 if (!is_last) {
234 70487 pthread_mutex_lock(&ctx->done_mutex);
235
2/2
✓ Branch 0 taken 70485 times.
✓ Branch 1 taken 70487 times.
140972 while (!ctx->done)
236 70485 pthread_cond_wait(&ctx->done_cond, &ctx->done_mutex);
237 70487 ctx->done = 0;
238 70487 pthread_mutex_unlock(&ctx->done_mutex);
239 }
240
241
1/2
✓ Branch 0 taken 75388 times.
✗ Branch 1 not taken.
75388 if (!ret)
242 75388 ret = atomic_load_explicit(&ctx->error, memory_order_relaxed);
243
244 75388 return ret;
245 }
246
247 184289 av_cold void avpriv_slicethread_free(AVSliceThread **pctx)
248 {
249 184289 AVSliceThread *ctx = *pctx;
250 int nb_workers, i;
251
252
2/2
✓ Branch 0 taken 177710 times.
✓ Branch 1 taken 6579 times.
184289 if (!ctx)
253 177710 return;
254
255 6579 nb_workers = ctx->nb_threads;
256
1/2
✓ Branch 0 taken 6579 times.
✗ Branch 1 not taken.
6579 if (!ctx->main_func)
257 6579 nb_workers--;
258
259 6579 ctx->finished = 1;
260
2/2
✓ Branch 0 taken 52329 times.
✓ Branch 1 taken 6579 times.
58908 for (i = 0; i < nb_workers; i++) {
261 52329 WorkerContext *w = &ctx->workers[i];
262 52329 pthread_mutex_lock(&w->mutex);
263 52329 w->done = 0;
264 52329 pthread_cond_signal(&w->cond);
265 52329 pthread_mutex_unlock(&w->mutex);
266 }
267
268
2/2
✓ Branch 0 taken 52329 times.
✓ Branch 1 taken 6579 times.
58908 for (i = 0; i < nb_workers; i++) {
269 52329 WorkerContext *w = &ctx->workers[i];
270 52329 pthread_join(w->thread, NULL);
271 52329 pthread_cond_destroy(&w->cond);
272 52329 pthread_mutex_destroy(&w->mutex);
273 }
274
275 6579 pthread_cond_destroy(&ctx->done_cond);
276 6579 pthread_mutex_destroy(&ctx->done_mutex);
277 6579 av_freep(&ctx->workers);
278 6579 av_freep(pctx);
279 }
280
281 /**
282 * Backwards compatibility wrapper for the deprecated avpriv_ slicethread API.
283 */
284
285 #if LIBAVUTIL_VERSION_MAJOR < 62
286
287 static int wrapper_worker(void *priv, int jobnr, int threadnr, int nb_jobs, int nb_threads)
288 {
289 AVSliceThread *ctx = priv;
290 ctx->worker_func_v1(ctx->priv_v1, jobnr, threadnr, nb_jobs, nb_threads);
291 return 0;
292 }
293
294 static int wrapper_main(void *priv)
295 {
296 AVSliceThread *ctx = priv;
297 ctx->main_func_v1(ctx->priv_v1);
298 return 0;
299 }
300
301 int avpriv_slicethread_create(AVSliceThread **pctx, void *priv,
302 void (*worker_func)(void *priv, int jobnr, int threadnr, int nb_jobs, int nb_threads),
303 void (*main_func)(void *priv),
304 int nb_threads)
305 {
306 int ret = avpriv_slicethread_create2(pctx, NULL, wrapper_worker,
307 main_func ? wrapper_main : NULL,
308 nb_threads);
309 if (ret < 0)
310 return ret;
311
312 (*pctx)->priv = *pctx;
313 (*pctx)->priv_v1 = priv;
314 (*pctx)->worker_func_v1 = worker_func;
315 (*pctx)->main_func_v1 = main_func;
316 return ret;
317 }
318
319 void avpriv_slicethread_execute(AVSliceThread *ctx, int nb_jobs, int execute_main)
320 {
321 avpriv_slicethread_execute2(ctx, nb_jobs, execute_main);
322 }
323
324 #endif /* LIBAVUTIL_VERSION_MAJOR < 62 */
325
326 #else /* HAVE_PTHREADS || HAVE_W32THREADS || HAVE_OS32THREADS */
327
328 int avpriv_slicethread_create2(AVSliceThread **pctx, void *priv,
329 int (*worker_func)(void *priv, int jobnr, int threadnr, int nb_jobs, int nb_threads),
330 int (*main_func)(void *priv),
331 int nb_threads)
332 {
333 *pctx = NULL;
334 return AVERROR(ENOSYS);
335 }
336
337 int avpriv_slicethread_execute2(AVSliceThread *ctx, int nb_jobs, int execute_main)
338 {
339 av_assert0(0);
340 }
341
342 void avpriv_slicethread_free(AVSliceThread **pctx)
343 {
344 av_assert0(!pctx || !*pctx);
345 }
346
347 #if LIBAVUTIL_VERSION_MAJOR < 62
348
349 int avpriv_slicethread_create(AVSliceThread **pctx, void *priv,
350 void (*worker_func)(void *priv, int jobnr, int threadnr, int nb_jobs, int nb_threads),
351 void (*main_func)(void *priv),
352 int nb_threads)
353 {
354 *pctx = NULL;
355 return AVERROR(ENOSYS);
356 }
357
358 void avpriv_slicethread_execute(AVSliceThread *ctx, int nb_jobs, int execute_main)
359 {
360 av_assert0(0);
361 }
362
363 #endif /* LIBAVUTIL_VERSION_MAJOR < 62 */
364
365 #endif /* HAVE_PTHREADS || HAVE_W32THREADS || HAVE_OS32THREADS */
366