FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavutil/executor.c
Date: 2024-05-03 15:42:48
Exec Total Coverage
Lines: 75 85 88.2%
Functions: 8 8 100.0%
Branches: 34 52 65.4%

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 "mem.h"
24 #include "thread.h"
25
26 #include "executor.h"
27
28 #if !HAVE_THREADS
29
30 #define ExecutorThread char
31
32 #define executor_thread_create(t, a, s, ar) 0
33 #define executor_thread_join(t, r) do {} while(0)
34
35 #else
36
37 #define ExecutorThread pthread_t
38
39 #define executor_thread_create(t, a, s, ar) pthread_create(t, a, s, ar)
40 #define executor_thread_join(t, r) pthread_join(t, r)
41
42 #endif //!HAVE_THREADS
43
44 typedef struct ThreadInfo {
45 AVExecutor *e;
46 ExecutorThread thread;
47 } ThreadInfo;
48
49 struct AVExecutor {
50 AVTaskCallbacks cb;
51 int thread_count;
52
53 ThreadInfo *threads;
54 uint8_t *local_contexts;
55
56 AVMutex lock;
57 AVCond cond;
58 int die;
59
60 AVTask *tasks;
61 };
62
63 208030 static AVTask* remove_task(AVTask **prev, AVTask *t)
64 {
65 208030 *prev = t->next;
66 208030 t->next = NULL;
67 208030 return t;
68 }
69
70 208030 static void add_task(AVTask **prev, AVTask *t)
71 {
72 208030 t->next = *prev;
73 208030 *prev = t;
74 208030 }
75
76 208114 static int run_one_task(AVExecutor *e, void *lc)
77 {
78 208114 AVTaskCallbacks *cb = &e->cb;
79 AVTask **prev;
80
81
3/4
✓ Branch 0 taken 208030 times.
✓ Branch 1 taken 84 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 208030 times.
208114 for (prev = &e->tasks; *prev && !cb->ready(*prev, cb->user_data); prev = &(*prev)->next)
82 /* nothing */;
83
2/2
✓ Branch 0 taken 208030 times.
✓ Branch 1 taken 84 times.
208114 if (*prev) {
84 208030 AVTask *t = remove_task(prev, *prev);
85 208030 ff_mutex_unlock(&e->lock);
86 208030 cb->run(t, lc, cb->user_data);
87 208030 ff_mutex_lock(&e->lock);
88 208030 return 1;
89 }
90 84 return 0;
91 }
92
93 #if HAVE_THREADS
94 50 static void *executor_worker_task(void *data)
95 {
96 50 ThreadInfo *ti = (ThreadInfo*)data;
97 50 AVExecutor *e = ti->e;
98 50 void *lc = e->local_contexts + (ti - e->threads) * e->cb.local_context_size;
99
100 50 ff_mutex_lock(&e->lock);
101 while (1) {
102
2/2
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 208114 times.
208164 if (e->die) break;
103
104
2/2
✓ Branch 1 taken 84 times.
✓ Branch 2 taken 208030 times.
208114 if (!run_one_task(e, lc)) {
105 //no task in one loop
106 84 ff_cond_wait(&e->cond, &e->lock);
107 }
108 }
109 50 ff_mutex_unlock(&e->lock);
110 50 return NULL;
111 }
112 #endif
113
114 50 static void executor_free(AVExecutor *e, const int has_lock, const int has_cond)
115 {
116
1/2
✓ Branch 0 taken 50 times.
✗ Branch 1 not taken.
50 if (e->thread_count) {
117 //signal die
118 50 ff_mutex_lock(&e->lock);
119 50 e->die = 1;
120 50 ff_cond_broadcast(&e->cond);
121 50 ff_mutex_unlock(&e->lock);
122
123
2/2
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 50 times.
100 for (int i = 0; i < e->thread_count; i++)
124 50 executor_thread_join(e->threads[i].thread, NULL);
125 }
126
1/2
✓ Branch 0 taken 50 times.
✗ Branch 1 not taken.
50 if (has_cond)
127 50 ff_cond_destroy(&e->cond);
128
1/2
✓ Branch 0 taken 50 times.
✗ Branch 1 not taken.
50 if (has_lock)
129 50 ff_mutex_destroy(&e->lock);
130
131 50 av_free(e->threads);
132 50 av_free(e->local_contexts);
133
134 50 av_free(e);
135 50 }
136
137 50 AVExecutor* av_executor_alloc(const AVTaskCallbacks *cb, int thread_count)
138 {
139 AVExecutor *e;
140 50 int has_lock = 0, has_cond = 0;
141
5/10
✓ Branch 0 taken 50 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 50 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 50 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 50 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 50 times.
50 if (!cb || !cb->user_data || !cb->ready || !cb->run || !cb->priority_higher)
142 return NULL;
143
144 50 e = av_mallocz(sizeof(*e));
145
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 50 times.
50 if (!e)
146 return NULL;
147 50 e->cb = *cb;
148
149 50 e->local_contexts = av_calloc(thread_count, e->cb.local_context_size);
150
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 50 times.
50 if (!e->local_contexts)
151 goto free_executor;
152
153 50 e->threads = av_calloc(thread_count, sizeof(*e->threads));
154
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 50 times.
50 if (!e->threads)
155 goto free_executor;
156
157 50 has_lock = !ff_mutex_init(&e->lock, NULL);
158 50 has_cond = !ff_cond_init(&e->cond, NULL);
159
160
2/4
✓ Branch 0 taken 50 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 50 times.
50 if (!has_lock || !has_cond)
161 goto free_executor;
162
163
2/2
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 50 times.
100 for (/* nothing */; e->thread_count < thread_count; e->thread_count++) {
164 50 ThreadInfo *ti = e->threads + e->thread_count;
165 50 ti->e = e;
166
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 50 times.
50 if (executor_thread_create(&ti->thread, NULL, executor_worker_task, ti))
167 goto free_executor;
168 }
169 50 return e;
170
171 free_executor:
172 executor_free(e, has_lock, has_cond);
173 return NULL;
174 }
175
176 50 void av_executor_free(AVExecutor **executor)
177 {
178
2/4
✓ Branch 0 taken 50 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 50 times.
50 if (!executor || !*executor)
179 return;
180 50 executor_free(*executor, 1, 1);
181 50 *executor = NULL;
182 }
183
184 208030 void av_executor_execute(AVExecutor *e, AVTask *t)
185 {
186 208030 AVTaskCallbacks *cb = &e->cb;
187 AVTask **prev;
188
189 208030 ff_mutex_lock(&e->lock);
190
1/2
✓ Branch 0 taken 208030 times.
✗ Branch 1 not taken.
208030 if (t) {
191
4/4
✓ Branch 0 taken 2424530 times.
✓ Branch 1 taken 3642 times.
✓ Branch 3 taken 2220142 times.
✓ Branch 4 taken 204388 times.
2428172 for (prev = &e->tasks; *prev && cb->priority_higher(*prev, t); prev = &(*prev)->next)
192 /* nothing */;
193 208030 add_task(prev, t);
194 }
195 208030 ff_cond_signal(&e->cond);
196 208030 ff_mutex_unlock(&e->lock);
197
198 #if !HAVE_THREADS
199 // We are running in a single-threaded environment, so we must handle all tasks ourselves
200 while (run_one_task(e, e->local_contexts))
201 /* nothing */;
202 #endif
203 208030 }
204