FFmpeg
Loading...
Searching...
No Matches
slicethread.c
Go to the documentation of this file.
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
31typedef struct WorkerContext {
35 pthread_t thread;
36 int done;
37} WorkerContext;
38
39struct 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;
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
64static int run_jobs(AVSliceThread *ctx)
65{
66 unsigned nb_jobs = ctx->nb_jobs;
67 unsigned nb_active_threads = ctx->nb_active_threads;
68 unsigned first_job = atomic_fetch_add_explicit(&ctx->first_job, 1, memory_order_acq_rel);
69 unsigned current_job = first_job;
70
71 do {
72 int ret = atomic_load_explicit(&ctx->error, memory_order_relaxed);
73 if (ret)
74 continue;
75 ret = ctx->worker_func(ctx->priv, current_job, first_job, nb_jobs, nb_active_threads);
76 if (ret) {
77 int prev = 0;
79 memory_order_relaxed,
80 memory_order_relaxed);
81 }
82 } while ((current_job = atomic_fetch_add_explicit(&ctx->current_job, 1, memory_order_acq_rel)) < nb_jobs);
83
84 return current_job == nb_jobs + nb_active_threads - 1;
85}
86
87static void *attribute_align_arg thread_worker(void *v)
88{
89 WorkerContext *w = v;
90 AVSliceThread *ctx = w->ctx;
91
92 pthread_mutex_lock(&w->mutex);
93 pthread_cond_signal(&w->cond);
94
95 while (1) {
96 w->done = 1;
97 while (w->done)
98 pthread_cond_wait(&w->cond, &w->mutex);
99
100 if (ctx->finished) {
101 pthread_mutex_unlock(&w->mutex);
102 return NULL;
103 }
104
105 if (run_jobs(ctx)) {
106 pthread_mutex_lock(&ctx->done_mutex);
107 ctx->done = 1;
108 pthread_cond_signal(&ctx->done_cond);
109 pthread_mutex_unlock(&ctx->done_mutex);
110 }
111 }
112}
113
115int 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{
121 int nb_workers, i;
122 int ret;
123
124 av_assert0(nb_threads >= 0);
125 if (!nb_threads) {
126 int nb_cpus = av_cpu_count();
127 if (nb_cpus > 1)
128 nb_threads = FFMIN(nb_cpus + 1, MAX_AUTO_THREADS);
129 else
130 nb_threads = 1;
131 }
132
133 nb_workers = nb_threads;
134 if (!main_func)
135 nb_workers--;
136
137 *pctx = ctx = av_mallocz(sizeof(*ctx));
138 if (!ctx)
139 return AVERROR(ENOMEM);
140
141 if (nb_workers && !(ctx->workers = av_calloc(nb_workers, sizeof(*ctx->workers)))) {
142 av_freep(pctx);
143 return AVERROR(ENOMEM);
144 }
145
146 ctx->priv = priv;
147 ctx->worker_func = worker_func;
148 ctx->main_func = main_func;
149 ctx->nb_threads = nb_threads;
150 ctx->nb_active_threads = 0;
151 ctx->nb_jobs = 0;
152 ctx->finished = 0;
153
154 atomic_init(&ctx->first_job, 0);
155 atomic_init(&ctx->current_job, 0);
156 ret = pthread_mutex_init(&ctx->done_mutex, NULL);
157 if (ret) {
158 av_freep(&ctx->workers);
159 av_freep(pctx);
160 return AVERROR(ret);
161 }
162 ret = pthread_cond_init(&ctx->done_cond, NULL);
163 if (ret) {
164 ctx->nb_threads = main_func ? 0 : 1;
166 return AVERROR(ret);
167 }
168 ctx->done = 0;
169
170 for (i = 0; i < nb_workers; i++) {
171 WorkerContext *w = &ctx->workers[i];
172 w->ctx = ctx;
173 ret = pthread_mutex_init(&w->mutex, NULL);
174 if (ret) {
175 ctx->nb_threads = main_func ? i : i + 1;
177 return AVERROR(ret);
178 }
179 ret = pthread_cond_init(&w->cond, NULL);
180 if (ret) {
181 pthread_mutex_destroy(&w->mutex);
182 ctx->nb_threads = main_func ? i : i + 1;
184 return AVERROR(ret);
185 }
186 pthread_mutex_lock(&w->mutex);
187 w->done = 0;
188
189 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);
195 return AVERROR(ret);
196 }
197
198 while (!w->done)
199 pthread_cond_wait(&w->cond, &w->mutex);
200 pthread_mutex_unlock(&w->mutex);
201 }
202
203 return nb_threads;
204}
205
206int avpriv_slicethread_execute2(AVSliceThread *ctx, int nb_jobs, int execute_main)
207{
208 int nb_workers, i, is_last = 0, ret = 0;
209
210 av_assert0(nb_jobs > 0);
211 ctx->nb_jobs = nb_jobs;
212 ctx->nb_active_threads = FFMIN(nb_jobs, ctx->nb_threads);
213 atomic_store_explicit(&ctx->error, 0, memory_order_relaxed);
214 atomic_store_explicit(&ctx->first_job, 0, memory_order_relaxed);
215 atomic_store_explicit(&ctx->current_job, ctx->nb_active_threads, memory_order_relaxed);
216 nb_workers = ctx->nb_active_threads;
217 if (!ctx->main_func || !execute_main)
218 nb_workers--;
219
220 for (i = 0; i < nb_workers; i++) {
221 WorkerContext *w = &ctx->workers[i];
222 pthread_mutex_lock(&w->mutex);
223 w->done = 0;
224 pthread_cond_signal(&w->cond);
225 pthread_mutex_unlock(&w->mutex);
226 }
227
228 if (ctx->main_func && execute_main) {
229 ret = ctx->main_func(ctx->priv);
230 } else
231 is_last = run_jobs(ctx);
232
233 if (!is_last) {
234 pthread_mutex_lock(&ctx->done_mutex);
235 while (!ctx->done)
236 pthread_cond_wait(&ctx->done_cond, &ctx->done_mutex);
237 ctx->done = 0;
238 pthread_mutex_unlock(&ctx->done_mutex);
239 }
240
241 if (!ret)
242 ret = atomic_load_explicit(&ctx->error, memory_order_relaxed);
243
244 return ret;
245}
246
248{
249 AVSliceThread *ctx = *pctx;
250 int nb_workers, i;
251
252 if (!ctx)
253 return;
254
255 nb_workers = ctx->nb_threads;
256 if (!ctx->main_func)
257 nb_workers--;
258
259 ctx->finished = 1;
260 for (i = 0; i < nb_workers; i++) {
261 WorkerContext *w = &ctx->workers[i];
262 pthread_mutex_lock(&w->mutex);
263 w->done = 0;
264 pthread_cond_signal(&w->cond);
265 pthread_mutex_unlock(&w->mutex);
266 }
267
268 for (i = 0; i < nb_workers; i++) {
269 WorkerContext *w = &ctx->workers[i];
270 pthread_join(w->thread, NULL);
271 pthread_cond_destroy(&w->cond);
272 pthread_mutex_destroy(&w->mutex);
273 }
274
275 pthread_cond_destroy(&ctx->done_cond);
276 pthread_mutex_destroy(&ctx->done_mutex);
277 av_freep(&ctx->workers);
278 av_freep(pctx);
279}
280
281#else /* HAVE_PTHREADS || HAVE_W32THREADS || HAVE_OS32THREADS */
282
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
292int avpriv_slicethread_execute2(AVSliceThread *ctx, int nb_jobs, int execute_main)
293{
294 av_assert0(0);
295}
296
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
310static 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
317static int wrapper_main(void *priv)
318{
319 AVSliceThread *ctx = priv;
320 ctx->main_func_v1(ctx->priv_v1);
321 return 0;
322}
323
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{
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
342void 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 */
static AVFormatContext * ctx
simple assert() macros that are a bit more flexible than ISO C assert().
#define av_assert0(cond)
assert() equivalent, that is always enabled.
Definition avassert.h:42
#define i(width, name, range_min, range_max)
Definition cbs_h264.c:63
#define NULL
Definition coverity.c:32
#define atomic_fetch_add_explicit(object, operand, order)
Definition stdatomic.h:149
#define atomic_compare_exchange_strong_explicit(object, expected, desired, success, failure)
Definition stdatomic.h:123
intptr_t atomic_int
Definition stdatomic.h:55
#define atomic_load_explicit(object, order)
Definition stdatomic.h:96
intptr_t atomic_uint
Definition stdatomic.h:56
#define atomic_store_explicit(object, desired, order)
Definition stdatomic.h:90
#define atomic_init(obj, value)
Definition stdatomic.h:33
#define AVERROR(e)
Definition error.h:45
#define av_cold
Definition attributes.h:117
int av_cpu_count(void)
Definition cpu.c:228
common internal API header
#define attribute_align_arg
Definition internal.h:50
uint8_t w
Definition llvidencdsp.c:39
#define FFMIN(a, b)
Definition macros.h:49
void * av_calloc(size_t nmemb, size_t size)
Definition mem.c:264
Memory handling functions.
static av_always_inline int pthread_cond_signal(pthread_cond_t *cond)
Definition os2threads.h:152
static av_always_inline int pthread_mutex_lock(pthread_mutex_t *mutex)
Definition os2threads.h:119
static av_always_inline int pthread_cond_destroy(pthread_cond_t *cond)
Definition os2threads.h:144
static av_always_inline int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr)
Definition os2threads.h:104
static av_always_inline int pthread_join(pthread_t thread, void **value_ptr)
Definition os2threads.h:94
static av_always_inline int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr)
Definition os2threads.h:133
static av_always_inline int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
Definition os2threads.h:80
_fmutex pthread_mutex_t
Definition os2threads.h:53
static av_always_inline int pthread_mutex_unlock(pthread_mutex_t *mutex)
Definition os2threads.h:126
static av_always_inline int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
Definition os2threads.h:192
static av_always_inline int pthread_mutex_destroy(pthread_mutex_t *mutex)
Definition os2threads.h:112
int(* cond)(enum AVPixelFormat pix_fmt)
#define MAX_AUTO_THREADS
static int worker_func(void *priv, int jobnr, int threadnr, int nb_jobs, int nb_threads)
int main_func(AVCodecContext *c)
static AVMutex mutex
Definition resman.c:61
void avpriv_slicethread_execute(AVSliceThread *ctx, int nb_jobs, int execute_main)
static int wrapper_worker(void *priv, int jobnr, int threadnr, int nb_jobs, int nb_threads)
Backwards compatibility wrapper for the deprecated avpriv_ slicethread API.
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)
static int wrapper_main(void *priv)
void avpriv_slicethread_free(AVSliceThread **pctx)
Destroy slice threading context.
int avpriv_slicethread_execute2(AVSliceThread *ctx, int nb_jobs, int execute_main)
Execute slice threading.
int avpriv_slicethread_create2(AVSliceThread **pctx, void *priv, int(*worker_func)(void *priv, int jobnr, int threadnr, int nb_jobs, int nb_threads), int(*main_func)(void *priv), int nb_threads)
Create slice threading context.
struct AVSliceThread AVSliceThread
Definition slicethread.h:25
#define av_mallocz(s)
#define av_freep(p)
static void error(const char *err)
CONDITION_VARIABLE pthread_cond_t
Definition w32pthreads.h:58