Coverage Report

Created: 2026-09-14 08:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ffmpeg/libavcodec/executor.c
Line
Count
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
0
#define executor_thread_create(t, a, s, ar)      pthread_create(t, a, s, ar)
42
0
#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
static FFTask* remove_task(Queue *q)
72
6.29M
{
73
6.29M
    FFTask *t = q->head;
74
6.29M
    if (t) {
75
2.05M
        q->head = t->next;
76
2.05M
        t->next = NULL;
77
2.05M
        if (!q->head)
78
1.55M
            q->tail = NULL;
79
2.05M
    }
80
6.29M
    return t;
81
6.29M
}
82
83
static void add_task(Queue *q, FFTask *t)
84
2.05M
{
85
2.05M
    t->next = NULL;
86
2.05M
    if (!q->head)
87
1.55M
        q->tail = q->head = t;
88
502k
    else
89
502k
        q->tail = q->tail->next = t;
90
2.05M
}
91
92
static int run_one_task(FFExecutor *e, void *lc)
93
3.29M
{
94
3.29M
    FFTaskCallbacks *cb = &e->cb;
95
3.29M
    FFTask *t = NULL;
96
97
9.59M
    for (int i = 0; i < e->cb.priorities && !t; i++)
98
6.29M
        t = remove_task(e->q + i);
99
100
3.29M
    if (t) {
101
2.05M
        if (e->thread_count > 0)
102
0
            ff_mutex_unlock(&e->lock);
103
2.05M
        cb->run(t, lc, cb->user_data);
104
2.05M
        if (e->thread_count > 0)
105
0
            ff_mutex_lock(&e->lock);
106
2.05M
        return 1;
107
2.05M
    }
108
1.24M
    return 0;
109
3.29M
}
110
111
#if HAVE_THREADS
112
static void *executor_worker_task(void *data)
113
0
{
114
0
    ThreadInfo *ti = (ThreadInfo*)data;
115
0
    FFExecutor *e  = ti->e;
116
0
    void *lc       = e->local_contexts + (ti - e->threads) * e->cb.local_context_size;
117
118
0
    ff_mutex_lock(&e->lock);
119
0
    while (1) {
120
0
        if (e->die) break;
121
122
0
        if (!run_one_task(e, lc)) {
123
            //no task in one loop
124
0
            ff_cond_wait(&e->cond, &e->lock);
125
0
        }
126
0
    }
127
0
    ff_mutex_unlock(&e->lock);
128
0
    return NULL;
129
0
}
130
#endif
131
132
static av_cold void executor_free(FFExecutor *e, const int has_lock, const int has_cond)
133
15.7k
{
134
15.7k
    if (e->thread_count) {
135
        //signal die
136
0
        ff_mutex_lock(&e->lock);
137
0
        e->die = 1;
138
0
        ff_cond_broadcast(&e->cond);
139
0
        ff_mutex_unlock(&e->lock);
140
141
0
        for (int i = 0; i < e->thread_count; i++)
142
0
            executor_thread_join(e->threads[i].thread, NULL);
143
0
    }
144
15.7k
    if (has_cond)
145
0
        ff_cond_destroy(&e->cond);
146
15.7k
    if (has_lock)
147
0
        ff_mutex_destroy(&e->lock);
148
149
15.7k
    av_free(e->threads);
150
15.7k
    av_free(e->q);
151
15.7k
    av_free(e->local_contexts);
152
153
15.7k
    av_free(e);
154
15.7k
}
155
156
av_cold FFExecutor* ff_executor_alloc(const FFTaskCallbacks *cb, int thread_count)
157
15.7k
{
158
15.7k
    FFExecutor *e;
159
15.7k
    int has_lock = 0, has_cond = 0;
160
15.7k
    if (!cb || !cb->user_data || !cb->run || !cb->priorities)
161
0
        return NULL;
162
163
15.7k
    e = av_mallocz(sizeof(*e));
164
15.7k
    if (!e)
165
0
        return NULL;
166
15.7k
    e->cb = *cb;
167
168
15.7k
    e->local_contexts = av_calloc(FFMAX(thread_count, 1), e->cb.local_context_size);
169
15.7k
    if (!e->local_contexts)
170
0
        goto free_executor;
171
172
15.7k
    e->q = av_calloc(e->cb.priorities, sizeof(Queue));
173
15.7k
    if (!e->q)
174
0
        goto free_executor;
175
176
15.7k
    e->threads = av_calloc(FFMAX(thread_count, 1), sizeof(*e->threads));
177
15.7k
    if (!e->threads)
178
0
        goto free_executor;
179
180
15.7k
    if (!thread_count)
181
15.7k
        return e;
182
183
0
    has_lock = !ff_mutex_init(&e->lock, NULL);
184
0
    has_cond = !ff_cond_init(&e->cond, NULL);
185
186
0
    if (!has_lock || !has_cond)
187
0
        goto free_executor;
188
189
0
    for (/* nothing */; e->thread_count < thread_count; e->thread_count++) {
190
0
        ThreadInfo *ti = e->threads + e->thread_count;
191
0
        ti->e = e;
192
0
        if (executor_thread_create(&ti->thread, NULL, executor_worker_task, ti))
193
0
            goto free_executor;
194
0
    }
195
0
    return e;
196
197
0
free_executor:
198
0
    executor_free(e, has_lock, has_cond);
199
0
    return NULL;
200
0
}
201
202
av_cold void ff_executor_free(FFExecutor **executor)
203
15.7k
{
204
15.7k
    int thread_count;
205
206
15.7k
    if (!executor || !*executor)
207
47
        return;
208
15.7k
    thread_count = (*executor)->thread_count;
209
15.7k
    executor_free(*executor, thread_count, thread_count);
210
15.7k
    *executor = NULL;
211
15.7k
}
212
213
void ff_executor_execute(FFExecutor *e, FFTask *t)
214
2.05M
{
215
2.05M
    if (e->thread_count)
216
0
        ff_mutex_lock(&e->lock);
217
2.05M
    if (t)
218
2.05M
        add_task(e->q + t->priority % e->cb.priorities, t);
219
2.05M
    if (e->thread_count) {
220
0
        ff_cond_signal(&e->cond);
221
0
        ff_mutex_unlock(&e->lock);
222
0
    }
223
224
2.05M
    if (!e->thread_count || !HAVE_THREADS) {
225
2.05M
        if (e->recursive)
226
809k
            return;
227
1.24M
        e->recursive = true;
228
        // We are running in a single-threaded environment, so we must handle all tasks ourselves
229
3.29M
        while (run_one_task(e, e->local_contexts))
230
2.05M
            /* nothing */;
231
        e->recursive = false;
232
1.24M
    }
233
2.05M
}