Coverage Report

Created: 2026-05-16 06:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/work/svt-av1/Source/Lib/Codec/svt_threads.c
Line
Count
Source
1
/*
2
* Copyright(c) 2019 Intel Corporation
3
*
4
* This source code is subject to the terms of the BSD 2 Clause License and
5
* the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
6
* was not distributed with this source code in the LICENSE file, you can
7
* obtain it at https://www.aomedia.org/license/software-license. If the Alliance for Open
8
* Media Patent License 1.0 was not distributed with this source code in the
9
* PATENTS file, you can obtain it at https://www.aomedia.org/license/patent-license.
10
*/
11
12
// Summary:
13
// EbThreads contains wrappers functions that hide
14
// platform specific objects such as threads, semaphores,
15
// and mutexs.  The goal is to eliminiate platform #define
16
// in the code.
17
18
#include "EbSvtAv1.h"
19
#if defined(__has_feature)
20
#if __has_feature(thread_sanitizer)
21
#define EB_THREAD_SANITIZER_ENABLED 1
22
#endif
23
#endif
24
25
#ifndef EB_THREAD_SANITIZER_ENABLED
26
2
#define EB_THREAD_SANITIZER_ENABLED 0
27
#endif
28
29
/****************************************
30
 * Universal Includes
31
 ****************************************/
32
#include <stdbool.h>
33
#include <stdlib.h>
34
#include <string.h>
35
#include "svt_threads.h"
36
#include "svt_log.h"
37
#if SVT_AV1_NVTX
38
#include "svt_nvtx.h"
39
#include <sys/syscall.h>
40
#endif
41
/****************************************
42
  * Win32 Includes
43
  ****************************************/
44
#ifdef _WIN32
45
#include <windows.h>
46
#else
47
#include <stdio.h>
48
#include <errno.h>
49
#include <fcntl.h>
50
#include <pthread.h>
51
#include <semaphore.h>
52
#include <unistd.h>
53
#endif // _WIN32
54
#ifdef __APPLE__
55
#include <dispatch/dispatch.h>
56
#endif
57
#if PRINTF_TIME
58
#include <time.h>
59
#ifdef _WIN32
60
void printfTime(const char* fmt, ...) {
61
    va_list args;
62
    va_start(args, fmt);
63
    SVT_LOG("  [%i ms]\t", ((int32_t)clock()));
64
    vprintf(fmt, args);
65
    va_end(args);
66
}
67
#endif
68
#endif
69
70
#ifndef _WIN32
71
0
static void* dummy_func(void* arg) {
72
0
    (void)arg;
73
0
    return NULL;
74
0
}
75
76
/*
77
 * pthread_setname_np has different signatures across platforms; the trampoline
78
 * always invokes this from inside the new thread, so Apple's self-only form is
79
 * naturally compatible.
80
 */
81
13.3k
static inline void svt_thread_self_setname(const char* name) {
82
#if defined(__APPLE__)
83
    (void)pthread_setname_np(name);
84
#elif defined(__linux__) || defined(__GLIBC__) || defined(__ANDROID__)
85
    (void)pthread_setname_np(pthread_self(), name);
86
#else
87
    (void)name;
88
#endif
89
13.3k
}
90
91
/*
92
 * Self-naming trampoline. nsys snapshots the thread name early (often before a
93
 * spawner-side pthread_setname_np lands), so we let the new thread rename
94
 * itself before it enters user_fn. This makes svt-* names visible in Nsight
95
 * timelines, /proc/<tid>/comm, and ps/top.
96
 */
97
typedef struct SvtThreadStart {
98
    void* (*fn)(void*);
99
    void* arg;
100
    char  name[16];
101
} SvtThreadStart;
102
103
13.3k
static void* svt_thread_trampoline(void* p) {
104
13.3k
    SvtThreadStart* payload = (SvtThreadStart*)p;
105
13.3k
    void* (*fn)(void*)      = payload->fn;
106
13.3k
    void* arg               = payload->arg;
107
13.3k
    char  name[16];
108
13.3k
    strncpy(name, payload->name, sizeof(name) - 1);
109
13.3k
    name[sizeof(name) - 1] = '\0';
110
13.3k
    free(payload);
111
112
13.3k
    if (name[0]) {
113
13.3k
        svt_thread_self_setname(name);
114
#if SVT_AV1_NVTX
115
        // syscall(SYS_gettid) instead of gettid(): gettid() needs glibc 2.30+
116
        // (Aug 2019); the raw syscall works on older glibc and musl too.
117
        SVT_NVTX_NAME_OS_THREAD((unsigned long)syscall(SYS_gettid), name);
118
#endif
119
13.3k
    }
120
121
13.3k
    return fn(arg);
122
13.3k
}
123
124
// These can stay with pthread_once_t since this is specific to pthreads implementation
125
static pthread_once_t checked_once = PTHREAD_ONCE_INIT;
126
static bool           can_use_prio = false;
127
128
1
static void check_set_prio(void) {
129
    /* We can only use realtime priority if we are running as root, so
130
     * check if geteuid() == 0 (meaning either root or sudo).
131
     * If we don't do this check, we will eventually run into memory
132
     * issues if the encoder is uninitialized and re-initialized multiple
133
     * times in one executable due to a bug in glibc.
134
     * https://sourceware.org/bugzilla/show_bug.cgi?id=19511
135
     *
136
     * We still need to exclude the case of thread sanitizer because we
137
     * run the test as root inside the container and trying to change
138
     * the thread priority will __always__ fail the thread sanitizer.
139
     * https://github.com/google/sanitizers/issues/1088
140
     */
141
1
    if (EB_THREAD_SANITIZER_ENABLED || geteuid() != 0) {
142
0
        return;
143
0
    }
144
1
    pthread_attr_t attr;
145
1
    int            ret;
146
1
    if ((ret = pthread_attr_init(&attr))) {
147
0
        SVT_WARN("Failed to initialize thread attributes: %s\n", strerror(ret));
148
0
        return;
149
0
    }
150
1
    struct sched_param param;
151
1
    if ((ret = pthread_attr_getschedparam(&attr, &param))) {
152
0
        SVT_WARN("Failed to get thread priority: %s\n", strerror(ret));
153
0
        goto end;
154
0
    }
155
1
    param.sched_priority = 99;
156
1
    if ((ret = pthread_attr_setschedparam(&attr, &param))) {
157
1
        SVT_WARN("Failed to set thread priority: %s\n", strerror(ret));
158
1
        goto end;
159
1
    }
160
0
    pthread_t th;
161
0
    if ((ret = pthread_create(&th, &attr, dummy_func, NULL))) {
162
0
        SVT_WARN("Failed to create thread: %s\n", strerror(ret));
163
0
        goto end;
164
0
    }
165
0
    can_use_prio = true;
166
0
    pthread_join(th, NULL);
167
1
end:
168
1
    if ((ret = pthread_attr_destroy(&attr))) {
169
0
        SVT_WARN("Failed to destroy thread attributes: %s\n", strerror(ret));
170
0
    }
171
1
}
172
#endif
173
174
10.5k
void svt_format_thread_name(char* buf, size_t size, const char* prefix, uint32_t index) {
175
10.5k
    snprintf(buf, size, "%s%u", prefix, index);
176
10.5k
}
177
178
/****************************************
179
 * svt_create_thread
180
 ****************************************/
181
13.3k
EbHandle svt_create_thread(void* thread_function(void*), void* thread_context, const char* name) {
182
13.3k
    EbHandle thread_handle = NULL;
183
184
    // Drop the `svt_aom_` prefix that EB_CREATE_THREAD pulls in via
185
    // `#thread_function`. Linux's TASK_COMM_LEN is 15 chars; without the strip
186
    // `svt_aom_picture_decision_kernel` and `svt_aom_picture_manager_kernel`
187
    // collapse to the same `svt_aom_picture` label in /proc/.../comm and the
188
    // Nsight ThreadNames table.
189
13.3k
    if (name && !strncmp(name, "svt_aom_", 8)) {
190
2.84k
        name += 8;
191
2.84k
    }
192
193
#ifdef _WIN32
194
    thread_handle = (EbHandle)CreateThread(
195
        NULL, // default security attributes
196
        0, // default stack size
197
        (LPTHREAD_START_ROUTINE)thread_function, // function to be tied to the new thread
198
        thread_context, // context to be tied to the new thread
199
        0, // thread active when created
200
        NULL); // new thread ID
201
202
    // SetThreadDescription (Windows 10 1607+) — best effort. Older Windows
203
    // returns E_NOTIMPL; nothing else we can do here.
204
    if (thread_handle && name && *name) {
205
        // Mirror Linux's TASK_COMM_LEN (15 + NUL); MultiByteToWideChar fails if
206
        // the source doesn't fit, so truncate first.
207
        char    truncated[16];
208
        wchar_t wname[16];
209
        strncpy(truncated, name, sizeof(truncated) - 1);
210
        truncated[sizeof(truncated) - 1] = '\0';
211
        if (MultiByteToWideChar(CP_UTF8, 0, truncated, -1, wname, (int)(sizeof(wname) / sizeof(wname[0]))) > 0) {
212
            (void)SetThreadDescription((HANDLE)thread_handle, wname);
213
        }
214
    }
215
216
#else
217
13.3k
    if (pthread_once(&checked_once, check_set_prio)) {
218
0
        SVT_ERROR("Failed to run pthread_once to check if we can set priority\n");
219
0
        return NULL;
220
0
    }
221
222
13.3k
    pthread_attr_t attr;
223
13.3k
    if (pthread_attr_init(&attr)) {
224
0
        SVT_ERROR("Failed to initialize thread attributes\n");
225
0
        return NULL;
226
0
    }
227
228
13.3k
    if (can_use_prio) {
229
        // As described in https://docs.oracle.com/cd/E19455-01/806-5257/attrib-16/index.html
230
0
        struct sched_param param;
231
0
        pthread_attr_getschedparam(&attr, &param);
232
0
        param.sched_priority = 99;
233
0
        pthread_attr_setschedparam(&attr, &param);
234
0
    }
235
236
    // 1 MiB in bytes for now since we can't easily change the stack size after creation
237
13.3k
    const size_t min_stack_size = 1024 * 1024;
238
    // We don't care if this fails, it's just a hint for the min size we are expecting.
239
13.3k
    (void)pthread_attr_setstacksize(&attr, min_stack_size);
240
241
13.3k
    pthread_t* th = malloc(sizeof(*th));
242
13.3k
    if (th == NULL) {
243
0
        SVT_ERROR("Failed to allocate thread handle\n");
244
0
        pthread_attr_destroy(&attr);
245
0
        return NULL;
246
0
    }
247
248
13.3k
    SvtThreadStart* payload = malloc(sizeof(*payload));
249
13.3k
    if (payload == NULL) {
250
0
        SVT_ERROR("Failed to allocate thread start payload\n");
251
0
        free(th);
252
0
        pthread_attr_destroy(&attr);
253
0
        return NULL;
254
0
    }
255
13.3k
    payload->fn  = thread_function;
256
13.3k
    payload->arg = thread_context;
257
13.3k
    if (name && *name) {
258
13.3k
        strncpy(payload->name, name, sizeof(payload->name) - 1);
259
13.3k
        payload->name[sizeof(payload->name) - 1] = '\0';
260
13.3k
    } else {
261
0
        payload->name[0] = '\0';
262
0
    }
263
264
13.3k
    int ret;
265
13.3k
    if ((ret = pthread_create(th, &attr, svt_thread_trampoline, payload))) {
266
0
        SVT_ERROR("Failed to create thread: %s\n", strerror(ret));
267
0
        free(payload);
268
0
        free(th);
269
0
        pthread_attr_destroy(&attr);
270
0
        return NULL;
271
0
    }
272
273
13.3k
    pthread_attr_destroy(&attr);
274
275
13.3k
    thread_handle = th;
276
13.3k
#endif // _WIN32
277
278
13.3k
    return thread_handle;
279
13.3k
}
280
281
/****************************************
282
 * svt_destroy_thread
283
 ****************************************/
284
13.3k
EbErrorType svt_destroy_thread(EbHandle thread_handle) {
285
13.3k
    EbErrorType error_return;
286
287
#ifdef _WIN32
288
    WaitForSingleObject(thread_handle, INFINITE);
289
    error_return = CloseHandle(thread_handle) ? EB_ErrorNone : EB_ErrorDestroyThreadFailed;
290
#else
291
13.3k
    error_return = pthread_join(*((pthread_t*)thread_handle), NULL) ? EB_ErrorDestroyThreadFailed : EB_ErrorNone;
292
13.3k
    free(thread_handle);
293
13.3k
#endif // _WIN32
294
295
13.3k
    return error_return;
296
13.3k
}
297
298
/***************************************
299
 * svt_create_semaphore
300
 ***************************************/
301
55.4k
EbHandle svt_create_semaphore(uint32_t initial_count, uint32_t max_count) {
302
55.4k
    EbHandle semaphore_handle;
303
304
#if defined(_WIN32)
305
    semaphore_handle = (EbHandle)CreateSemaphore(NULL, // default security attributes
306
                                                 initial_count, // initial semaphore count
307
                                                 max_count, // maximum semaphore count
308
                                                 NULL); // semaphore is not named
309
#elif defined(__APPLE__)
310
    UNUSED(max_count);
311
    semaphore_handle = (EbHandle)dispatch_semaphore_create(initial_count);
312
#else
313
55.4k
    UNUSED(max_count);
314
315
55.4k
    semaphore_handle = (sem_t*)malloc(sizeof(sem_t));
316
55.4k
    if (semaphore_handle != NULL) {
317
55.4k
        sem_init((sem_t*)semaphore_handle, // semaphore handle
318
55.4k
                 0, // shared semaphore (not local)
319
55.4k
                 initial_count); // initial count
320
55.4k
    }
321
55.4k
#endif
322
323
55.4k
    return semaphore_handle;
324
55.4k
}
325
326
/***************************************
327
 * svt_post_semaphore
328
 ***************************************/
329
48.3k
EbErrorType svt_post_semaphore(EbHandle semaphore_handle) {
330
48.3k
    EbErrorType return_error;
331
332
#ifdef _WIN32
333
    return_error = !ReleaseSemaphore(semaphore_handle, // semaphore handle
334
                                     1, // amount to increment the semaphore
335
                                     NULL) // pointer to previous count (optional)
336
        ? EB_ErrorSemaphoreUnresponsive
337
        : EB_ErrorNone;
338
#elif defined(__APPLE__)
339
    dispatch_semaphore_signal((dispatch_semaphore_t)semaphore_handle);
340
    return_error = EB_ErrorNone;
341
#else
342
48.3k
    return_error = sem_post((sem_t*)semaphore_handle) ? EB_ErrorSemaphoreUnresponsive : EB_ErrorNone;
343
48.3k
#endif
344
345
48.3k
    return return_error;
346
48.3k
}
347
348
/***************************************
349
 * svt_block_on_semaphore
350
 ***************************************/
351
48.3k
EbErrorType svt_block_on_semaphore(EbHandle semaphore_handle) {
352
48.3k
    EbErrorType return_error;
353
354
#ifdef _WIN32
355
    return_error = WaitForSingleObject((HANDLE)semaphore_handle, INFINITE) ? EB_ErrorSemaphoreUnresponsive
356
                                                                           : EB_ErrorNone;
357
#elif defined(__APPLE__)
358
    return_error = dispatch_semaphore_wait((dispatch_semaphore_t)semaphore_handle, DISPATCH_TIME_FOREVER)
359
        ? EB_ErrorSemaphoreUnresponsive
360
        : EB_ErrorNone;
361
#else
362
48.3k
    int ret;
363
48.3k
    do {
364
48.3k
        ret = sem_wait((sem_t*)semaphore_handle);
365
48.3k
    } while (ret == -1 && errno == EINTR);
366
48.3k
    return_error = ret ? EB_ErrorSemaphoreUnresponsive : EB_ErrorNone;
367
48.3k
#endif
368
369
48.3k
    return return_error;
370
48.3k
}
371
372
/***************************************
373
 * svt_destroy_semaphore
374
 ***************************************/
375
55.4k
EbErrorType svt_destroy_semaphore(EbHandle semaphore_handle) {
376
55.4k
    EbErrorType return_error;
377
378
#ifdef _WIN32
379
    return_error = !CloseHandle((HANDLE)semaphore_handle) ? EB_ErrorDestroySemaphoreFailed : EB_ErrorNone;
380
#elif defined(__APPLE__)
381
    dispatch_release((dispatch_semaphore_t)semaphore_handle);
382
    return_error = EB_ErrorNone;
383
#else
384
55.4k
    return_error = sem_destroy((sem_t*)semaphore_handle) ? EB_ErrorDestroySemaphoreFailed : EB_ErrorNone;
385
55.4k
    free(semaphore_handle);
386
55.4k
#endif
387
388
55.4k
    return return_error;
389
55.4k
}
390
391
/***************************************
392
 * svt_create_mutex
393
 ***************************************/
394
373k
EbHandle svt_create_mutex(void) {
395
373k
    EbHandle mutex_handle;
396
397
#ifdef _WIN32
398
    mutex_handle = (EbHandle)CreateMutex(NULL, // default security attributes
399
                                         false, // false := not initially owned
400
                                         NULL); // mutex is not named
401
402
#else
403
404
373k
    mutex_handle = (EbHandle)malloc(sizeof(pthread_mutex_t));
405
406
373k
    if (mutex_handle != NULL) {
407
373k
        pthread_mutex_init((pthread_mutex_t*)mutex_handle,
408
373k
                           NULL); // default attributes
409
373k
    }
410
373k
#endif
411
412
373k
    return mutex_handle;
413
373k
}
414
415
/***************************************
416
 * svt_release_mutex
417
 ***************************************/
418
359k
EbErrorType svt_release_mutex(EbHandle mutex_handle) {
419
359k
    EbErrorType return_error;
420
421
#ifdef _WIN32
422
    return_error = !ReleaseMutex((HANDLE)mutex_handle) ? EB_ErrorMutexUnresponsive : EB_ErrorNone;
423
#else
424
359k
    return_error = pthread_mutex_unlock((pthread_mutex_t*)mutex_handle) ? EB_ErrorMutexUnresponsive : EB_ErrorNone;
425
359k
#endif
426
427
359k
    return return_error;
428
359k
}
429
430
/***************************************
431
 * svt_block_on_mutex
432
 ***************************************/
433
359k
EbErrorType svt_block_on_mutex(EbHandle mutex_handle) {
434
359k
    EbErrorType return_error;
435
436
#ifdef _WIN32
437
    return_error = WaitForSingleObject((HANDLE)mutex_handle, INFINITE) ? EB_ErrorMutexUnresponsive : EB_ErrorNone;
438
#else
439
359k
    return_error = pthread_mutex_lock((pthread_mutex_t*)mutex_handle) ? EB_ErrorMutexUnresponsive : EB_ErrorNone;
440
359k
#endif
441
442
359k
    return return_error;
443
359k
}
444
445
/***************************************
446
 * svt_destroy_mutex
447
 ***************************************/
448
373k
EbErrorType svt_destroy_mutex(EbHandle mutex_handle) {
449
373k
    EbErrorType return_error;
450
451
#ifdef _WIN32
452
    return_error = CloseHandle((HANDLE)mutex_handle) ? EB_ErrorDestroyMutexFailed : EB_ErrorNone;
453
#else
454
373k
    return_error = pthread_mutex_destroy((pthread_mutex_t*)mutex_handle) ? EB_ErrorDestroyMutexFailed : EB_ErrorNone;
455
373k
    free(mutex_handle);
456
373k
#endif
457
458
373k
    return return_error;
459
373k
}
460
461
/*
462
    set an atomic variable to an input value
463
*/
464
948
void svt_aom_atomic_set_u32(AtomicVarU32* var, uint32_t in) {
465
948
    svt_block_on_mutex(var->mutex);
466
948
    var->obj = in;
467
948
    svt_release_mutex(var->mutex);
468
948
}
469
470
/*
471
    create condition variable
472
473
    Condition variables are synchronization primitives that enable
474
    threads to wait until a particular condition occurs.
475
    Condition variables enable threads to atomically release
476
    a lock(mutex) and enter the sleeping state.
477
    it could be seen as a combined: wait and release mutex
478
*/
479
948
EbErrorType svt_create_cond_var(CondVar* cond_var) {
480
948
    EbErrorType return_error;
481
948
    cond_var->val = 0;
482
#ifdef _WIN32
483
    InitializeCriticalSection(&cond_var->cs);
484
    InitializeConditionVariable(&cond_var->cv);
485
    return_error = EB_ErrorNone;
486
#else
487
948
    pthread_mutex_init(&cond_var->m_mutex, NULL);
488
948
    return_error = pthread_cond_init(&cond_var->m_cond, NULL);
489
490
948
#endif
491
948
    return return_error;
492
948
}
493
494
/*
495
    set a  condition variable to the new value
496
*/
497
0
EbErrorType svt_set_cond_var(CondVar* cond_var, int32_t newval) {
498
0
    EbErrorType return_error;
499
#ifdef _WIN32
500
    EnterCriticalSection(&cond_var->cs);
501
    cond_var->val = newval;
502
    WakeAllConditionVariable(&cond_var->cv);
503
    LeaveCriticalSection(&cond_var->cs);
504
    return_error = EB_ErrorNone;
505
#else
506
0
    return_error  = pthread_mutex_lock(&cond_var->m_mutex);
507
0
    cond_var->val = newval;
508
0
    return_error |= pthread_cond_broadcast(&cond_var->m_cond);
509
0
    return_error |= pthread_mutex_unlock(&cond_var->m_mutex);
510
0
#endif
511
0
    return return_error;
512
0
}
513
514
/*
515
    wait until the cond variable changes to a value
516
    different than input
517
*/
518
519
0
EbErrorType svt_wait_cond_var(CondVar* cond_var, int32_t input) {
520
#ifdef _WIN32
521
522
    EnterCriticalSection(&cond_var->cs);
523
    while (cond_var->val == input) {
524
        SleepConditionVariableCS(&cond_var->cv, &cond_var->cs, INFINITE);
525
    }
526
    LeaveCriticalSection(&cond_var->cs);
527
#else
528
0
    if (pthread_mutex_lock(&cond_var->m_mutex)) {
529
0
        return EB_ErrorMutexUnresponsive;
530
0
    }
531
0
    while (cond_var->val == input) {
532
0
        if (pthread_cond_wait(&cond_var->m_cond, &cond_var->m_mutex)) {
533
0
            (void)pthread_mutex_unlock(&cond_var->m_mutex);
534
0
            return EB_ErrorMutexUnresponsive;
535
0
        }
536
0
    }
537
0
    if (pthread_mutex_unlock(&cond_var->m_mutex)) {
538
0
        return EB_ErrorMutexUnresponsive;
539
0
    }
540
0
#endif
541
0
    return EB_ErrorNone;
542
0
}
543
544
13.2k
void svt_run_once(OnceType* once_control, OnceFn init_routine) {
545
#ifdef _WIN32
546
    InitOnceExecuteOnce(once_control, init_routine, NULL, NULL);
547
#else
548
13.2k
    pthread_once(once_control, init_routine);
549
13.2k
#endif
550
13.2k
}