FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavcodec/executor.c
Date: 2024-11-20 23:03:26
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 413522 static FFTask* remove_task(Queue *q)
72 {
73 413522 FFTask *t = q->head;
74
2/2
✓ Branch 0 taken 228253 times.
✓ Branch 1 taken 185269 times.
413522 if (t) {
75 228253 q->head = t->next;
76 228253 t->next = NULL;
77
2/2
✓ Branch 0 taken 41720 times.
✓ Branch 1 taken 186533 times.
228253 if (!q->head)
78 41720 q->tail = NULL;
79 }
80 413522 return t;
81 }
82
83 228253 static void add_task(Queue *q, FFTask *t)
84 {
85 228253 t->next = NULL;
86
2/2
✓ Branch 0 taken 41720 times.
✓ Branch 1 taken 186533 times.
228253 if (!q->head)
87 41720 q->tail = q->head = t;
88 else
89 186533 q->tail = q->tail->next = t;
90 228253 }
91
92 229189 static int run_one_task(FFExecutor *e, void *lc)
93 {
94 229189 FFTaskCallbacks *cb = &e->cb;
95 229189 FFTask *t = NULL;
96
97
4/4
✓ Branch 0 taken 641775 times.
✓ Branch 1 taken 936 times.
✓ Branch 2 taken 413522 times.
✓ Branch 3 taken 228253 times.
642711 for (int i = 0; i < e->cb.priorities && !t; i++)
98 413522 t = remove_task(e->q + i);
99
100
2/2
✓ Branch 0 taken 228253 times.
✓ Branch 1 taken 936 times.
229189 if (t) {
101
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 228253 times.
228253 if (e->thread_count > 0)
102 ff_mutex_unlock(&e->lock);
103 228253 cb->run(t, lc, cb->user_data);
104
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 228253 times.
228253 if (e->thread_count > 0)
105 ff_mutex_lock(&e->lock);
106 228253 return 1;
107 }
108 936 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 82 static void executor_free(FFExecutor *e, const int has_lock, const int has_cond)
133 {
134
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 82 times.
82 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 82 times.
82 if (has_cond)
145 ff_cond_destroy(&e->cond);
146
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 82 times.
82 if (has_lock)
147 ff_mutex_destroy(&e->lock);
148
149 82 av_free(e->threads);
150 82 av_free(e->q);
151 82 av_free(e->local_contexts);
152
153 82 av_free(e);
154 82 }
155
156 82 FFExecutor* ff_executor_alloc(const FFTaskCallbacks *cb, int thread_count)
157 {
158 FFExecutor *e;
159 82 int has_lock = 0, has_cond = 0;
160
4/8
✓ Branch 0 taken 82 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 82 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 82 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 82 times.
82 if (!cb || !cb->user_data || !cb->run || !cb->priorities)
161 return NULL;
162
163 82 e = av_mallocz(sizeof(*e));
164
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 82 times.
82 if (!e)
165 return NULL;
166 82 e->cb = *cb;
167
168 82 e->local_contexts = av_calloc(FFMAX(thread_count, 1), e->cb.local_context_size);
169
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 82 times.
82 if (!e->local_contexts)
170 goto free_executor;
171
172 82 e->q = av_calloc(e->cb.priorities, sizeof(Queue));
173
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 82 times.
82 if (!e->q)
174 goto free_executor;
175
176 82 e->threads = av_calloc(FFMAX(thread_count, 1), sizeof(*e->threads));
177
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 82 times.
82 if (!e->threads)
178 goto free_executor;
179
180
1/2
✓ Branch 0 taken 82 times.
✗ Branch 1 not taken.
82 if (!thread_count)
181 82 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 82 void ff_executor_free(FFExecutor **executor)
203 {
204 int thread_count;
205
206
2/4
✓ Branch 0 taken 82 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 82 times.
82 if (!executor || !*executor)
207 return;
208 82 thread_count = (*executor)->thread_count;
209 82 executor_free(*executor, thread_count, thread_count);
210 82 *executor = NULL;
211 }
212
213 228253 void ff_executor_execute(FFExecutor *e, FFTask *t)
214 {
215
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 228253 times.
228253 if (e->thread_count)
216 ff_mutex_lock(&e->lock);
217
1/2
✓ Branch 0 taken 228253 times.
✗ Branch 1 not taken.
228253 if (t)
218 228253 add_task(e->q + t->priority % e->cb.priorities, t);
219
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 228253 times.
228253 if (e->thread_count) {
220 ff_cond_signal(&e->cond);
221 ff_mutex_unlock(&e->lock);
222 }
223
224
1/2
✓ Branch 0 taken 228253 times.
✗ Branch 1 not taken.
228253 if (!e->thread_count || !HAVE_THREADS) {
225
2/2
✓ Branch 0 taken 227317 times.
✓ Branch 1 taken 936 times.
228253 if (e->recursive)
226 227317 return;
227 936 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 228253 times.
✓ Branch 2 taken 936 times.
229189 while (run_one_task(e, e->local_contexts))
230 /* nothing */;
231 936 e->recursive = false;
232 }
233 }
234