FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavutil/slicethread.c
Date: 2026-08-28 18:35:09
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 659960 static int run_jobs(AVSliceThread *ctx)
65 {
66 659960 unsigned nb_jobs = ctx->nb_jobs;
67 659960 unsigned nb_active_threads = ctx->nb_active_threads;
68 659960 unsigned first_job = atomic_fetch_add_explicit(&ctx->first_job, 1, memory_order_acq_rel);
69 659960 unsigned current_job = first_job;
70
71 do {
72 662048 int ret = atomic_load_explicit(&ctx->error, memory_order_relaxed);
73
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 662048 times.
662048 if (ret)
74 continue;
75 662048 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 662048 times.
662048 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 659960 times.
662048 } while ((current_job = atomic_fetch_add_explicit(&ctx->current_job, 1, memory_order_acq_rel)) < nb_jobs);
83
84 659960 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 636899 w->done = 1;
97
2/2
✓ Branch 0 taken 636899 times.
✓ Branch 1 taken 636899 times.
1273798 while (w->done)
98 636899 pthread_cond_wait(&w->cond, &w->mutex);
99
100
2/2
✓ Branch 0 taken 52329 times.
✓ Branch 1 taken 584570 times.
636899 if (ctx->finished) {
101 52329 pthread_mutex_unlock(&w->mutex);
102 52329 return NULL;
103 }
104
105
2/2
✓ Branch 1 taken 70412 times.
✓ Branch 2 taken 514158 times.
584570 if (run_jobs(ctx)) {
106 70412 pthread_mutex_lock(&ctx->done_mutex);
107 70412 ctx->done = 1;
108 70412 pthread_cond_signal(&ctx->done_cond);
109 70412 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 75390 int avpriv_slicethread_execute2(AVSliceThread *ctx, int nb_jobs, int execute_main)
207 {
208 75390 int nb_workers, i, is_last = 0, ret = 0;
209
210
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 75390 times.
75390 av_assert0(nb_jobs > 0);
211 75390 ctx->nb_jobs = nb_jobs;
212 75390 ctx->nb_active_threads = FFMIN(nb_jobs, ctx->nb_threads);
213 75390 atomic_store_explicit(&ctx->error, 0, memory_order_relaxed);
214 75390 atomic_store_explicit(&ctx->first_job, 0, memory_order_relaxed);
215 75390 atomic_store_explicit(&ctx->current_job, ctx->nb_active_threads, memory_order_relaxed);
216 75390 nb_workers = ctx->nb_active_threads;
217
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 75390 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
75390 if (!ctx->main_func || !execute_main)
218 75390 nb_workers--;
219
220
2/2
✓ Branch 0 taken 584570 times.
✓ Branch 1 taken 75390 times.
659960 for (i = 0; i < nb_workers; i++) {
221 584570 WorkerContext *w = &ctx->workers[i];
222 584570 pthread_mutex_lock(&w->mutex);
223 584570 w->done = 0;
224 584570 pthread_cond_signal(&w->cond);
225 584570 pthread_mutex_unlock(&w->mutex);
226 }
227
228
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 75390 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
75390 if (ctx->main_func && execute_main) {
229 ret = ctx->main_func(ctx->priv);
230 } else
231 75390 is_last = run_jobs(ctx);
232
233
2/2
✓ Branch 0 taken 70412 times.
✓ Branch 1 taken 4978 times.
75390 if (!is_last) {
234 70412 pthread_mutex_lock(&ctx->done_mutex);
235
2/2
✓ Branch 0 taken 70412 times.
✓ Branch 1 taken 70412 times.
140824 while (!ctx->done)
236 70412 pthread_cond_wait(&ctx->done_cond, &ctx->done_mutex);
237 70412 ctx->done = 0;
238 70412 pthread_mutex_unlock(&ctx->done_mutex);
239 }
240
241
1/2
✓ Branch 0 taken 75390 times.
✗ Branch 1 not taken.
75390 if (!ret)
242 75390 ret = atomic_load_explicit(&ctx->error, memory_order_relaxed);
243
244 75390 return ret;
245 }
246
247 184284 av_cold void avpriv_slicethread_free(AVSliceThread **pctx)
248 {
249 184284 AVSliceThread *ctx = *pctx;
250 int nb_workers, i;
251
252
2/2
✓ Branch 0 taken 177705 times.
✓ Branch 1 taken 6579 times.
184284 if (!ctx)
253 177705 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 #else /* HAVE_PTHREADS || HAVE_W32THREADS || HAVE_OS32THREADS */
282
283 int avpriv_slicethread_create2(AVSliceThread **pctx, void *priv,
284 int (*worker_func)(void *priv, int jobnr, int threadnr, int nb_jobs, int nb_threads),
285 int (*main_func)(void *priv),
286 int nb_threads)
287 {
288 *pctx = NULL;
289 return AVERROR(ENOSYS);
290 }
291
292 int avpriv_slicethread_execute2(AVSliceThread *ctx, int nb_jobs, int execute_main)
293 {
294 av_assert0(0);
295 }
296
297 void avpriv_slicethread_free(AVSliceThread **pctx)
298 {
299 av_assert0(!pctx || !*pctx);
300 }
301
302 #endif /* HAVE_PTHREADS || HAVE_W32THREADS || HAVE_OS32THREADS */
303
304 /**
305 * Backwards compatibility wrapper for the deprecated avpriv_ slicethread API.
306 */
307
308 #if LIBAVUTIL_VERSION_MAJOR < 62
309
310 static int wrapper_worker(void *priv, int jobnr, int threadnr, int nb_jobs, int nb_threads)
311 {
312 AVSliceThread *ctx = priv;
313 ctx->worker_func_v1(ctx->priv_v1, jobnr, threadnr, nb_jobs, nb_threads);
314 return 0;
315 }
316
317 static int wrapper_main(void *priv)
318 {
319 AVSliceThread *ctx = priv;
320 ctx->main_func_v1(ctx->priv_v1);
321 return 0;
322 }
323
324 int avpriv_slicethread_create(AVSliceThread **pctx, void *priv,
325 void (*worker_func)(void *priv, int jobnr, int threadnr, int nb_jobs, int nb_threads),
326 void (*main_func)(void *priv),
327 int nb_threads)
328 {
329 int ret = avpriv_slicethread_create2(pctx, NULL, wrapper_worker,
330 main_func ? wrapper_main : NULL,
331 nb_threads);
332 if (ret < 0)
333 return ret;
334
335 (*pctx)->priv = *pctx;
336 (*pctx)->priv_v1 = priv;
337 (*pctx)->worker_func_v1 = worker_func;
338 (*pctx)->main_func_v1 = main_func;
339 return ret;
340 }
341
342 void avpriv_slicethread_execute(AVSliceThread *ctx, int nb_jobs, int execute_main)
343 {
344 avpriv_slicethread_execute2(ctx, nb_jobs, execute_main);
345 }
346
347 #endif /* LIBAVUTIL_VERSION_MAJOR < 62 */
348