FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavutil/executor.c
Date: 2024-07-26 21:54:09
Exec Total Coverage
Lines: 56 97 57.7%
Functions: 7 8 87.5%
Branches: 33 68 48.5%

Line Branch Exec Source
1 /*
2 * Copyright (C) 2023 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 "mem.h"
26 #include "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 AVExecutor *e;
48 ExecutorThread thread;
49 } ThreadInfo;
50
51 struct AVExecutor {
52 AVTaskCallbacks cb;
53 int thread_count;
54 bool recursive;
55
56 ThreadInfo *threads;
57 uint8_t *local_contexts;
58
59 AVMutex lock;
60 AVCond cond;
61 int die;
62
63 AVTask *tasks;
64 };
65
66 211207 static AVTask* remove_task(AVTask **prev, AVTask *t)
67 {
68 211207 *prev = t->next;
69 211207 t->next = NULL;
70 211207 return t;
71 }
72
73 211207 static void add_task(AVTask **prev, AVTask *t)
74 {
75 211207 t->next = *prev;
76 211207 *prev = t;
77 211207 }
78
79 213134 static int run_one_task(AVExecutor *e, void *lc)
80 {
81 213134 AVTaskCallbacks *cb = &e->cb;
82 AVTask **prev;
83
84
3/4
✓ Branch 0 taken 211207 times.
✓ Branch 1 taken 1927 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 211207 times.
213134 for (prev = &e->tasks; *prev && !cb->ready(*prev, cb->user_data); prev = &(*prev)->next)
85 /* nothing */;
86
2/2
✓ Branch 0 taken 211207 times.
✓ Branch 1 taken 1927 times.
213134 if (*prev) {
87 211207 AVTask *t = remove_task(prev, *prev);
88
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 211207 times.
211207 if (e->thread_count > 0)
89 ff_mutex_unlock(&e->lock);
90 211207 cb->run(t, lc, cb->user_data);
91
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 211207 times.
211207 if (e->thread_count > 0)
92 ff_mutex_lock(&e->lock);
93 211207 return 1;
94 }
95 1927 return 0;
96 }
97
98 #if HAVE_THREADS
99 static void *executor_worker_task(void *data)
100 {
101 ThreadInfo *ti = (ThreadInfo*)data;
102 AVExecutor *e = ti->e;
103 void *lc = e->local_contexts + (ti - e->threads) * e->cb.local_context_size;
104
105 ff_mutex_lock(&e->lock);
106 while (1) {
107 if (e->die) break;
108
109 if (!run_one_task(e, lc)) {
110 //no task in one loop
111 ff_cond_wait(&e->cond, &e->lock);
112 }
113 }
114 ff_mutex_unlock(&e->lock);
115 return NULL;
116 }
117 #endif
118
119 52 static void executor_free(AVExecutor *e, const int has_lock, const int has_cond)
120 {
121
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 52 times.
52 if (e->thread_count) {
122 //signal die
123 ff_mutex_lock(&e->lock);
124 e->die = 1;
125 ff_cond_broadcast(&e->cond);
126 ff_mutex_unlock(&e->lock);
127
128 for (int i = 0; i < e->thread_count; i++)
129 executor_thread_join(e->threads[i].thread, NULL);
130 }
131
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 52 times.
52 if (has_cond)
132 ff_cond_destroy(&e->cond);
133
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 52 times.
52 if (has_lock)
134 ff_mutex_destroy(&e->lock);
135
136 52 av_free(e->threads);
137 52 av_free(e->local_contexts);
138
139 52 av_free(e);
140 52 }
141
142 52 AVExecutor* av_executor_alloc(const AVTaskCallbacks *cb, int thread_count)
143 {
144 AVExecutor *e;
145 52 int has_lock = 0, has_cond = 0;
146
5/10
✓ Branch 0 taken 52 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 52 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 52 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 52 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 52 times.
52 if (!cb || !cb->user_data || !cb->ready || !cb->run || !cb->priority_higher)
147 return NULL;
148
149 52 e = av_mallocz(sizeof(*e));
150
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 52 times.
52 if (!e)
151 return NULL;
152 52 e->cb = *cb;
153
154 52 e->local_contexts = av_calloc(FFMAX(thread_count, 1), e->cb.local_context_size);
155
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 52 times.
52 if (!e->local_contexts)
156 goto free_executor;
157
158 52 e->threads = av_calloc(FFMAX(thread_count, 1), sizeof(*e->threads));
159
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 52 times.
52 if (!e->threads)
160 goto free_executor;
161
162
1/2
✓ Branch 0 taken 52 times.
✗ Branch 1 not taken.
52 if (!thread_count)
163 52 return e;
164
165 has_lock = !ff_mutex_init(&e->lock, NULL);
166 has_cond = !ff_cond_init(&e->cond, NULL);
167
168 if (!has_lock || !has_cond)
169 goto free_executor;
170
171 for (/* nothing */; e->thread_count < thread_count; e->thread_count++) {
172 ThreadInfo *ti = e->threads + e->thread_count;
173 ti->e = e;
174 if (executor_thread_create(&ti->thread, NULL, executor_worker_task, ti))
175 goto free_executor;
176 }
177 return e;
178
179 free_executor:
180 executor_free(e, has_lock, has_cond);
181 return NULL;
182 }
183
184 52 void av_executor_free(AVExecutor **executor)
185 {
186 int thread_count;
187
188
2/4
✓ Branch 0 taken 52 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 52 times.
52 if (!executor || !*executor)
189 return;
190 52 thread_count = (*executor)->thread_count;
191 52 executor_free(*executor, thread_count, thread_count);
192 52 *executor = NULL;
193 }
194
195 211207 void av_executor_execute(AVExecutor *e, AVTask *t)
196 {
197 211207 AVTaskCallbacks *cb = &e->cb;
198 AVTask **prev;
199
200
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 211207 times.
211207 if (e->thread_count)
201 ff_mutex_lock(&e->lock);
202
1/2
✓ Branch 0 taken 211207 times.
✗ Branch 1 not taken.
211207 if (t) {
203
4/4
✓ Branch 0 taken 2104358 times.
✓ Branch 1 taken 36761 times.
✓ Branch 3 taken 1929912 times.
✓ Branch 4 taken 174446 times.
2141119 for (prev = &e->tasks; *prev && cb->priority_higher(*prev, t); prev = &(*prev)->next)
204 /* nothing */;
205 211207 add_task(prev, t);
206 }
207
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 211207 times.
211207 if (e->thread_count) {
208 ff_cond_signal(&e->cond);
209 ff_mutex_unlock(&e->lock);
210 }
211
212
1/2
✓ Branch 0 taken 211207 times.
✗ Branch 1 not taken.
211207 if (!e->thread_count || !HAVE_THREADS) {
213
2/2
✓ Branch 0 taken 209280 times.
✓ Branch 1 taken 1927 times.
211207 if (e->recursive)
214 209280 return;
215 1927 e->recursive = true;
216 // We are running in a single-threaded environment, so we must handle all tasks ourselves
217
2/2
✓ Branch 1 taken 211207 times.
✓ Branch 2 taken 1927 times.
213134 while (run_one_task(e, e->local_contexts))
218 /* nothing */;
219 1927 e->recursive = false;
220 }
221 }
222