FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavcodec/executor.c
Date: 2026-10-04 22:42:07
Exec Total Coverage
Lines: 64 106 60.4%
Functions: 7 8 87.5%
Branches: 36 70 51.4%

Line Branch Exec Source
1 /*
2 * Copyright (C) 2024 Nuo Mi
3 *
4 * This file is part of FFmpeg.
5 *
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include "config.h"
22
23 #include <stdbool.h>
24
25 #include "libavutil/mem.h"
26 #include "libavutil/thread.h"
27
28 #include "executor.h"
29
30 #if !HAVE_THREADS
31
32 #define ExecutorThread char
33
34 #define executor_thread_create(t, a, s, ar) 0
35 #define executor_thread_join(t, r) do {} while(0)
36
37 #else
38
39 #define ExecutorThread pthread_t
40
41 #define executor_thread_create(t, a, s, ar) pthread_create(t, a, s, ar)
42 #define executor_thread_join(t, r) pthread_join(t, r)
43
44 #endif //!HAVE_THREADS
45
46 typedef struct ThreadInfo {
47 FFExecutor *e;
48 ExecutorThread thread;
49 } ThreadInfo;
50
51 typedef struct Queue {
52 FFTask *head;
53 FFTask *tail;
54 } Queue;
55
56 struct FFExecutor {
57 FFTaskCallbacks cb;
58 int thread_count;
59 bool recursive;
60
61 ThreadInfo *threads;
62 uint8_t *local_contexts;
63
64 AVMutex lock;
65 AVCond cond;
66 int die;
67
68 Queue *q;
69 };
70
71 506574 static FFTask* remove_task(Queue *q)
72 {
73 506574 FFTask *t = q->head;
74
2/2
✓ Branch 0 taken 279500 times.
✓ Branch 1 taken 227074 times.
506574 if (t) {
75 279500 q->head = t->next;
76 279500 t->next = NULL;
77
2/2
✓ Branch 0 taken 49783 times.
✓ Branch 1 taken 229717 times.
279500 if (!q->head)
78 49783 q->tail = NULL;
79 }
80 506574 return t;
81 }
82
83 279500 static void add_task(Queue *q, FFTask *t)
84 {
85 279500 t->next = NULL;
86
2/2
✓ Branch 0 taken 49783 times.
✓ Branch 1 taken 229717 times.
279500 if (!q->head)
87 49783 q->tail = q->head = t;
88 else
89 229717 q->tail = q->tail->next = t;
90 279500 }
91
92 280636 static int run_one_task(FFExecutor *e, void *lc)
93 {
94 280636 FFTaskCallbacks *cb = &e->cb;
95 280636 FFTask *t = NULL;
96
97
4/4
✓ Branch 0 taken 786074 times.
✓ Branch 1 taken 1136 times.
✓ Branch 2 taken 506574 times.
✓ Branch 3 taken 279500 times.
787210 for (int i = 0; i < e->cb.priorities && !t; i++)
98 506574 t = remove_task(e->q + i);
99
100
2/2
✓ Branch 0 taken 279500 times.
✓ Branch 1 taken 1136 times.
280636 if (t) {
101
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 279500 times.
279500 if (e->thread_count > 0)
102 ✗ ff_mutex_unlock(&e->lock);
103 279500 cb->run(t, lc, cb->user_data);
104
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 279500 times.
279500 if (e->thread_count > 0)
105 ✗ ff_mutex_lock(&e->lock);
106 279500 return 1;
107 }
108 1136 return 0;
109 }
110
111 #if HAVE_THREADS
112 ✗ static void *executor_worker_task(void *data)
113 {
114 ✗ ThreadInfo *ti = (ThreadInfo*)data;
115 ✗ FFExecutor *e = ti->e;
116 ✗ void *lc = e->local_contexts + (ti - e->threads) * e->cb.local_context_size;
117
118 ✗ ff_mutex_lock(&e->lock);
119 while (1) {
120 ✗ if (e->die) break;
121
122 ✗ if (!run_one_task(e, lc)) {
123 //no task in one loop
124 ✗ ff_cond_wait(&e->cond, &e->lock);
125 }
126 }
127 ✗ ff_mutex_unlock(&e->lock);
128 ✗ return NULL;
129 }
130 #endif
131
132 106 static av_cold void executor_free(FFExecutor *e, const int has_lock, const int has_cond)
133 {
134
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 106 times.
106 if (e->thread_count) {
135 //signal die
136 ✗ ff_mutex_lock(&e->lock);
137 ✗ e->die = 1;
138 ✗ ff_cond_broadcast(&e->cond);
139 ✗ ff_mutex_unlock(&e->lock);
140
141 ✗ for (int i = 0; i < e->thread_count; i++)
142 ✗ executor_thread_join(e->threads[i].thread, NULL);
143 }
144
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 106 times.
106 if (has_cond)
145 ✗ ff_cond_destroy(&e->cond);
146
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 106 times.
106 if (has_lock)
147 ✗ ff_mutex_destroy(&e->lock);
148
149 106 av_free(e->threads);
150 106 av_free(e->q);
151 106 av_free(e->local_contexts);
152
153 106 av_free(e);
154 106 }
155
156 106 av_cold FFExecutor* ff_executor_alloc(const FFTaskCallbacks *cb, int thread_count)
157 {
158 FFExecutor *e;
159 106 int has_lock = 0, has_cond = 0;
160
4/8
✓ Branch 0 taken 106 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 106 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 106 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 106 times.
106 if (!cb || !cb->user_data || !cb->run || !cb->priorities)
161 ✗ return NULL;
162
163 106 e = av_mallocz(sizeof(*e));
164
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 106 times.
106 if (!e)
165 ✗ return NULL;
166 106 e->cb = *cb;
167
168 106 e->local_contexts = av_calloc(FFMAX(thread_count, 1), e->cb.local_context_size);
169
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 106 times.
106 if (!e->local_contexts)
170 ✗ goto free_executor;
171
172 106 e->q = av_calloc(e->cb.priorities, sizeof(Queue));
173
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 106 times.
106 if (!e->q)
174 ✗ goto free_executor;
175
176 106 e->threads = av_calloc(FFMAX(thread_count, 1), sizeof(*e->threads));
177
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 106 times.
106 if (!e->threads)
178 ✗ goto free_executor;
179
180
1/2
✓ Branch 0 taken 106 times.
✗ Branch 1 not taken.
106 if (!thread_count)
181 106 return e;
182
183 ✗ has_lock = !ff_mutex_init(&e->lock, NULL);
184 ✗ has_cond = !ff_cond_init(&e->cond, NULL);
185
186 ✗ if (!has_lock || !has_cond)
187 ✗ goto free_executor;
188
189 ✗ for (/* nothing */; e->thread_count < thread_count; e->thread_count++) {
190 ✗ ThreadInfo *ti = e->threads + e->thread_count;
191 ✗ ti->e = e;
192 ✗ if (executor_thread_create(&ti->thread, NULL, executor_worker_task, ti))
193 ✗ goto free_executor;
194 }
195 ✗ return e;
196
197 ✗ free_executor:
198 ✗ executor_free(e, has_lock, has_cond);
199 ✗ return NULL;
200 }
201
202 106 av_cold void ff_executor_free(FFExecutor **executor)
203 {
204 int thread_count;
205
206
2/4
✓ Branch 0 taken 106 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 106 times.
106 if (!executor || !*executor)
207 ✗ return;
208 106 thread_count = (*executor)->thread_count;
209 106 executor_free(*executor, thread_count, thread_count);
210 106 *executor = NULL;
211 }
212
213 279500 void ff_executor_execute(FFExecutor *e, FFTask *t)
214 {
215
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 279500 times.
279500 if (e->thread_count)
216 ✗ ff_mutex_lock(&e->lock);
217
1/2
✓ Branch 0 taken 279500 times.
✗ Branch 1 not taken.
279500 if (t)
218 279500 add_task(e->q + t->priority % e->cb.priorities, t);
219
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 279500 times.
279500 if (e->thread_count) {
220 ✗ ff_cond_signal(&e->cond);
221 ✗ ff_mutex_unlock(&e->lock);
222 }
223
224
1/2
✓ Branch 0 taken 279500 times.
✗ Branch 1 not taken.
279500 if (!e->thread_count || !HAVE_THREADS) {
225
2/2
✓ Branch 0 taken 278364 times.
✓ Branch 1 taken 1136 times.
279500 if (e->recursive)
226 278364 return;
227 1136 e->recursive = true;
228 // We are running in a single-threaded environment, so we must handle all tasks ourselves
229
2/2
✓ Branch 1 taken 279500 times.
✓ Branch 2 taken 1136 times.
280636 while (run_one_task(e, e->local_contexts))
230 /* nothing */;
231 1136 e->recursive = false;
232 }
233 }
234