FFmpeg coverage


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