FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavutil/slicethread.c
Date: 2024-05-03 15:42:48
Exec Total Coverage
Lines: 108 119 90.8%
Functions: 5 5 100.0%
Branches: 37 56 66.1%

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
52 void *priv;
53 void (*worker_func)(void *priv, int jobnr, int threadnr, int nb_jobs, int nb_threads);
54 void (*main_func)(void *priv);
55 };
56
57 133444 static int run_jobs(AVSliceThread *ctx)
58 {
59 133444 unsigned nb_jobs = ctx->nb_jobs;
60 133444 unsigned nb_active_threads = ctx->nb_active_threads;
61 133444 unsigned first_job = atomic_fetch_add_explicit(&ctx->first_job, 1, memory_order_acq_rel);
62 133444 unsigned current_job = first_job;
63
64 do {
65 133444 ctx->worker_func(ctx->priv, current_job, first_job, nb_jobs, nb_active_threads);
66
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 133444 times.
133444 } while ((current_job = atomic_fetch_add_explicit(&ctx->current_job, 1, memory_order_acq_rel)) < nb_jobs);
67
68 133444 return current_job == nb_jobs + nb_active_threads - 1;
69 }
70
71 23779 static void *attribute_align_arg thread_worker(void *v)
72 {
73 23779 WorkerContext *w = v;
74 23779 AVSliceThread *ctx = w->ctx;
75
76 23779 pthread_mutex_lock(&w->mutex);
77 23779 pthread_cond_signal(&w->cond);
78
79 while (1) {
80 139869 w->done = 1;
81
2/2
✓ Branch 0 taken 139869 times.
✓ Branch 1 taken 139869 times.
279738 while (w->done)
82 139869 pthread_cond_wait(&w->cond, &w->mutex);
83
84
2/2
✓ Branch 0 taken 23779 times.
✓ Branch 1 taken 116090 times.
139869 if (ctx->finished) {
85 23779 pthread_mutex_unlock(&w->mutex);
86 23779 return NULL;
87 }
88
89
2/2
✓ Branch 1 taken 10632 times.
✓ Branch 2 taken 105458 times.
116090 if (run_jobs(ctx)) {
90 10632 pthread_mutex_lock(&ctx->done_mutex);
91 10632 ctx->done = 1;
92 10632 pthread_cond_signal(&ctx->done_cond);
93 10632 pthread_mutex_unlock(&ctx->done_mutex);
94 }
95 }
96 }
97
98 2998 int avpriv_slicethread_create(AVSliceThread **pctx, void *priv,
99 void (*worker_func)(void *priv, int jobnr, int threadnr, int nb_jobs, int nb_threads),
100 void (*main_func)(void *priv),
101 int nb_threads)
102 {
103 AVSliceThread *ctx;
104 int nb_workers, i;
105
106
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2998 times.
2998 av_assert0(nb_threads >= 0);
107
2/2
✓ Branch 0 taken 2602 times.
✓ Branch 1 taken 396 times.
2998 if (!nb_threads) {
108 2602 int nb_cpus = av_cpu_count();
109
1/2
✓ Branch 0 taken 2602 times.
✗ Branch 1 not taken.
2602 if (nb_cpus > 1)
110 2602 nb_threads = FFMIN(nb_cpus + 1, MAX_AUTO_THREADS);
111 else
112 nb_threads = 1;
113 }
114
115 2998 nb_workers = nb_threads;
116
1/2
✓ Branch 0 taken 2998 times.
✗ Branch 1 not taken.
2998 if (!main_func)
117 2998 nb_workers--;
118
119 2998 *pctx = ctx = av_mallocz(sizeof(*ctx));
120
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2998 times.
2998 if (!ctx)
121 return AVERROR(ENOMEM);
122
123
2/4
✓ Branch 0 taken 2998 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 2998 times.
2998 if (nb_workers && !(ctx->workers = av_calloc(nb_workers, sizeof(*ctx->workers)))) {
124 av_freep(pctx);
125 return AVERROR(ENOMEM);
126 }
127
128 2998 ctx->priv = priv;
129 2998 ctx->worker_func = worker_func;
130 2998 ctx->main_func = main_func;
131 2998 ctx->nb_threads = nb_threads;
132 2998 ctx->nb_active_threads = 0;
133 2998 ctx->nb_jobs = 0;
134 2998 ctx->finished = 0;
135
136 2998 atomic_init(&ctx->first_job, 0);
137 2998 atomic_init(&ctx->current_job, 0);
138 2998 pthread_mutex_init(&ctx->done_mutex, NULL);
139 2998 pthread_cond_init(&ctx->done_cond, NULL);
140 2998 ctx->done = 0;
141
142
2/2
✓ Branch 0 taken 23779 times.
✓ Branch 1 taken 2998 times.
26777 for (i = 0; i < nb_workers; i++) {
143 23779 WorkerContext *w = &ctx->workers[i];
144 int ret;
145 23779 w->ctx = ctx;
146 23779 pthread_mutex_init(&w->mutex, NULL);
147 23779 pthread_cond_init(&w->cond, NULL);
148 23779 pthread_mutex_lock(&w->mutex);
149 23779 w->done = 0;
150
151
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 23779 times.
23779 if (ret = pthread_create(&w->thread, NULL, thread_worker, w)) {
152 ctx->nb_threads = main_func ? i : i + 1;
153 pthread_mutex_unlock(&w->mutex);
154 pthread_cond_destroy(&w->cond);
155 pthread_mutex_destroy(&w->mutex);
156 avpriv_slicethread_free(pctx);
157 return AVERROR(ret);
158 }
159
160
2/2
✓ Branch 0 taken 23779 times.
✓ Branch 1 taken 23779 times.
47558 while (!w->done)
161 23779 pthread_cond_wait(&w->cond, &w->mutex);
162 23779 pthread_mutex_unlock(&w->mutex);
163 }
164
165 2998 return nb_threads;
166 }
167
168 17354 void avpriv_slicethread_execute(AVSliceThread *ctx, int nb_jobs, int execute_main)
169 {
170 17354 int nb_workers, i, is_last = 0;
171
172
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17354 times.
17354 av_assert0(nb_jobs > 0);
173 17354 ctx->nb_jobs = nb_jobs;
174 17354 ctx->nb_active_threads = FFMIN(nb_jobs, ctx->nb_threads);
175 17354 atomic_store_explicit(&ctx->first_job, 0, memory_order_relaxed);
176 17354 atomic_store_explicit(&ctx->current_job, ctx->nb_active_threads, memory_order_relaxed);
177 17354 nb_workers = ctx->nb_active_threads;
178
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 17354 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
17354 if (!ctx->main_func || !execute_main)
179 17354 nb_workers--;
180
181
2/2
✓ Branch 0 taken 116090 times.
✓ Branch 1 taken 17354 times.
133444 for (i = 0; i < nb_workers; i++) {
182 116090 WorkerContext *w = &ctx->workers[i];
183 116090 pthread_mutex_lock(&w->mutex);
184 116090 w->done = 0;
185 116090 pthread_cond_signal(&w->cond);
186 116090 pthread_mutex_unlock(&w->mutex);
187 }
188
189
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 17354 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
17354 if (ctx->main_func && execute_main)
190 ctx->main_func(ctx->priv);
191 else
192 17354 is_last = run_jobs(ctx);
193
194
2/2
✓ Branch 0 taken 10632 times.
✓ Branch 1 taken 6722 times.
17354 if (!is_last) {
195 10632 pthread_mutex_lock(&ctx->done_mutex);
196
2/2
✓ Branch 0 taken 10632 times.
✓ Branch 1 taken 10632 times.
21264 while (!ctx->done)
197 10632 pthread_cond_wait(&ctx->done_cond, &ctx->done_mutex);
198 10632 ctx->done = 0;
199 10632 pthread_mutex_unlock(&ctx->done_mutex);
200 }
201 17354 }
202
203 16761 void avpriv_slicethread_free(AVSliceThread **pctx)
204 {
205 AVSliceThread *ctx;
206 int nb_workers, i;
207
208
3/4
✓ Branch 0 taken 16761 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 13763 times.
✓ Branch 3 taken 2998 times.
16761 if (!pctx || !*pctx)
209 13763 return;
210
211 2998 ctx = *pctx;
212 2998 nb_workers = ctx->nb_threads;
213
1/2
✓ Branch 0 taken 2998 times.
✗ Branch 1 not taken.
2998 if (!ctx->main_func)
214 2998 nb_workers--;
215
216 2998 ctx->finished = 1;
217
2/2
✓ Branch 0 taken 23779 times.
✓ Branch 1 taken 2998 times.
26777 for (i = 0; i < nb_workers; i++) {
218 23779 WorkerContext *w = &ctx->workers[i];
219 23779 pthread_mutex_lock(&w->mutex);
220 23779 w->done = 0;
221 23779 pthread_cond_signal(&w->cond);
222 23779 pthread_mutex_unlock(&w->mutex);
223 }
224
225
2/2
✓ Branch 0 taken 23779 times.
✓ Branch 1 taken 2998 times.
26777 for (i = 0; i < nb_workers; i++) {
226 23779 WorkerContext *w = &ctx->workers[i];
227 23779 pthread_join(w->thread, NULL);
228 23779 pthread_cond_destroy(&w->cond);
229 23779 pthread_mutex_destroy(&w->mutex);
230 }
231
232 2998 pthread_cond_destroy(&ctx->done_cond);
233 2998 pthread_mutex_destroy(&ctx->done_mutex);
234 2998 av_freep(&ctx->workers);
235 2998 av_freep(pctx);
236 }
237
238 #else /* HAVE_PTHREADS || HAVE_W32THREADS || HAVE_OS32THREADS */
239
240 int avpriv_slicethread_create(AVSliceThread **pctx, void *priv,
241 void (*worker_func)(void *priv, int jobnr, int threadnr, int nb_jobs, int nb_threads),
242 void (*main_func)(void *priv),
243 int nb_threads)
244 {
245 *pctx = NULL;
246 return AVERROR(ENOSYS);
247 }
248
249 void avpriv_slicethread_execute(AVSliceThread *ctx, int nb_jobs, int execute_main)
250 {
251 av_assert0(0);
252 }
253
254 void avpriv_slicethread_free(AVSliceThread **pctx)
255 {
256 av_assert0(!pctx || !*pctx);
257 }
258
259 #endif /* HAVE_PTHREADS || HAVE_W32THREADS || HAVE_OS32THREADS */
260