Coverage Report

Created: 2026-10-17 06:56

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
12.8k
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
12.8k
}
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
12.8k
static void* svt_thread_trampoline(void* p) {
104
12.8k
    SvtThreadStart* payload = (SvtThreadStart*)p;
105
12.8k
    void* (*fn)(void*)      = payload->fn;
106
12.8k
    void* arg               = payload->arg;
107
12.8k
    char  name[16];
108
12.8k
    strncpy(name, payload->name, sizeof(name) - 1);
109
12.8k
    name[sizeof(name) - 1] = '\0';
110
12.8k
    free(payload);
111
112
12.8k
    if (name[0]) {
113
12.8k
        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
12.8k
    }
120
121
12.8k
    return fn(arg);
122
12.8k
}
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
9.89k
void svt_format_thread_name(char* buf, size_t size, const char* prefix, uint32_t index) {
175
9.89k
    snprintf(buf, size, "%s%u", prefix, index);
176
9.89k
}
177
178
/****************************************
179
 * svt_create_thread
180
 ****************************************/
181
12.8k
EbHandle svt_create_thread(void* thread_function(void*), void* thread_context, const char* name) {
182
12.8k
    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
12.8k
    if (name && !strncmp(name, "svt_aom_", 8)) {
190
2.95k
        name += 8;
191
2.95k
    }
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_s(truncated, sizeof(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
            // On Windows Server 2016, Windows 10 LTSB 2016 and Windows 10
213
            // version 1607, SetThreadDescription is only available via Run Time
214
            // Dynamic Linking in KernelBase.dll.
215
            HMODULE kernel_base_handle = GetModuleHandle("KernelBase.dll");
216
            if (kernel_base_handle != NULL) {
217
                typedef HRESULT (*set_thread_description_t)(HANDLE hThread, PCWSTR lpThreadDescription);
218
                set_thread_description_t set_thread_description = (set_thread_description_t)GetProcAddress(
219
                    kernel_base_handle, "SetThreadDescription");
220
                if (set_thread_description != NULL) {
221
                    set_thread_description((HANDLE)thread_handle, wname);
222
                }
223
            }
224
        }
225
    }
226
227
#else
228
12.8k
    if (pthread_once(&checked_once, check_set_prio)) {
229
0
        SVT_ERROR("Failed to run pthread_once to check if we can set priority\n");
230
0
        return NULL;
231
0
    }
232
233
12.8k
    pthread_attr_t attr;
234
12.8k
    if (pthread_attr_init(&attr)) {
235
0
        SVT_ERROR("Failed to initialize thread attributes\n");
236
0
        return NULL;
237
0
    }
238
239
12.8k
    if (can_use_prio) {
240
        // As described in https://docs.oracle.com/cd/E19455-01/806-5257/attrib-16/index.html
241
0
        struct sched_param param;
242
0
        pthread_attr_getschedparam(&attr, &param);
243
0
        param.sched_priority = 99;
244
0
        pthread_attr_setschedparam(&attr, &param);
245
0
    }
246
247
    // 1 MiB in bytes for now since we can't easily change the stack size after creation
248
12.8k
    const size_t min_stack_size = 1024 * 1024;
249
    // We don't care if this fails, it's just a hint for the min size we are expecting.
250
12.8k
    (void)pthread_attr_setstacksize(&attr, min_stack_size);
251
252
12.8k
    pthread_t* th = malloc(sizeof(*th));
253
12.8k
    if (th == NULL) {
254
0
        SVT_ERROR("Failed to allocate thread handle\n");
255
0
        pthread_attr_destroy(&attr);
256
0
        return NULL;
257
0
    }
258
259
12.8k
    SvtThreadStart* payload = malloc(sizeof(*payload));
260
12.8k
    if (payload == NULL) {
261
0
        SVT_ERROR("Failed to allocate thread start payload\n");
262
0
        free(th);
263
0
        pthread_attr_destroy(&attr);
264
0
        return NULL;
265
0
    }
266
12.8k
    payload->fn  = thread_function;
267
12.8k
    payload->arg = thread_context;
268
12.8k
    if (name && *name) {
269
12.8k
        strncpy(payload->name, name, sizeof(payload->name) - 1);
270
12.8k
        payload->name[sizeof(payload->name) - 1] = '\0';
271
12.8k
    } else {
272
0
        payload->name[0] = '\0';
273
0
    }
274
275
12.8k
    int ret;
276
12.8k
    if ((ret = pthread_create(th, &attr, svt_thread_trampoline, payload))) {
277
0
        SVT_ERROR("Failed to create thread: %s\n", strerror(ret));
278
0
        free(payload);
279
0
        free(th);
280
0
        pthread_attr_destroy(&attr);
281
0
        return NULL;
282
0
    }
283
284
12.8k
    pthread_attr_destroy(&attr);
285
286
12.8k
    thread_handle = th;
287
12.8k
#endif // _WIN32
288
289
12.8k
    return thread_handle;
290
12.8k
}
291
292
/****************************************
293
 * svt_destroy_thread
294
 ****************************************/
295
12.8k
EbErrorType svt_destroy_thread(EbHandle thread_handle) {
296
12.8k
    EbErrorType error_return;
297
298
#ifdef _WIN32
299
    WaitForSingleObject(thread_handle, INFINITE);
300
    error_return = CloseHandle(thread_handle) ? EB_ErrorNone : EB_ErrorDestroyThreadFailed;
301
#else
302
12.8k
    error_return = pthread_join(*((pthread_t*)thread_handle), NULL) ? EB_ErrorDestroyThreadFailed : EB_ErrorNone;
303
12.8k
    free(thread_handle);
304
12.8k
#endif // _WIN32
305
306
12.8k
    return error_return;
307
12.8k
}
308
309
/***************************************
310
 * svt_create_semaphore
311
 ***************************************/
312
54.3k
EbHandle svt_create_semaphore(uint32_t initial_count, uint32_t max_count) {
313
54.3k
    EbHandle semaphore_handle;
314
315
#if defined(_WIN32)
316
    semaphore_handle = (EbHandle)CreateSemaphore(NULL, // default security attributes
317
                                                 initial_count, // initial semaphore count
318
                                                 max_count, // maximum semaphore count
319
                                                 NULL); // semaphore is not named
320
#elif defined(__APPLE__)
321
    UNUSED(max_count);
322
    // libdispatch traps when a semaphore is freed with a lower count than it was
323
    // created with, and the encoder can end a stream one count short (#2251).
324
    // Start at 0 and signal up to the initial count, so only the count matters,
325
    // as on the other platforms.
326
    semaphore_handle = (EbHandle)dispatch_semaphore_create(0);
327
    for (uint32_t i = 0; semaphore_handle != NULL && i < initial_count; i++) {
328
        dispatch_semaphore_signal((dispatch_semaphore_t)semaphore_handle);
329
    }
330
#else
331
54.3k
    UNUSED(max_count);
332
333
54.3k
    semaphore_handle = (sem_t*)malloc(sizeof(sem_t));
334
54.3k
    if (semaphore_handle != NULL) {
335
54.3k
        sem_init((sem_t*)semaphore_handle, // semaphore handle
336
54.3k
                 0, // shared semaphore (not local)
337
54.3k
                 initial_count); // initial count
338
54.3k
    }
339
54.3k
#endif
340
341
54.3k
    return semaphore_handle;
342
54.3k
}
343
344
/***************************************
345
 * svt_post_semaphore
346
 ***************************************/
347
48.9k
EbErrorType svt_post_semaphore(EbHandle semaphore_handle) {
348
48.9k
    EbErrorType return_error;
349
350
#ifdef _WIN32
351
    return_error = !ReleaseSemaphore(semaphore_handle, // semaphore handle
352
                                     1, // amount to increment the semaphore
353
                                     NULL) // pointer to previous count (optional)
354
        ? EB_ErrorSemaphoreUnresponsive
355
        : EB_ErrorNone;
356
#elif defined(__APPLE__)
357
    dispatch_semaphore_signal((dispatch_semaphore_t)semaphore_handle);
358
    return_error = EB_ErrorNone;
359
#else
360
48.9k
    return_error = sem_post((sem_t*)semaphore_handle) ? EB_ErrorSemaphoreUnresponsive : EB_ErrorNone;
361
48.9k
#endif
362
363
48.9k
    return return_error;
364
48.9k
}
365
366
/***************************************
367
 * svt_block_on_semaphore
368
 ***************************************/
369
48.9k
EbErrorType svt_block_on_semaphore(EbHandle semaphore_handle) {
370
48.9k
    EbErrorType return_error;
371
372
#ifdef _WIN32
373
    return_error = WaitForSingleObject((HANDLE)semaphore_handle, INFINITE) ? EB_ErrorSemaphoreUnresponsive
374
                                                                           : EB_ErrorNone;
375
#elif defined(__APPLE__)
376
    return_error = dispatch_semaphore_wait((dispatch_semaphore_t)semaphore_handle, DISPATCH_TIME_FOREVER)
377
        ? EB_ErrorSemaphoreUnresponsive
378
        : EB_ErrorNone;
379
#else
380
48.9k
    int ret;
381
48.9k
    do {
382
48.9k
        ret = sem_wait((sem_t*)semaphore_handle);
383
48.9k
    } while (ret == -1 && errno == EINTR);
384
48.9k
    return_error = ret ? EB_ErrorSemaphoreUnresponsive : EB_ErrorNone;
385
48.9k
#endif
386
387
48.9k
    return return_error;
388
48.9k
}
389
390
/***************************************
391
 * svt_destroy_semaphore
392
 ***************************************/
393
54.3k
EbErrorType svt_destroy_semaphore(EbHandle semaphore_handle) {
394
54.3k
    EbErrorType return_error;
395
396
#ifdef _WIN32
397
    return_error = !CloseHandle((HANDLE)semaphore_handle) ? EB_ErrorDestroySemaphoreFailed : EB_ErrorNone;
398
#elif defined(__APPLE__)
399
    dispatch_release((dispatch_semaphore_t)semaphore_handle);
400
    return_error = EB_ErrorNone;
401
#else
402
54.3k
    return_error = sem_destroy((sem_t*)semaphore_handle) ? EB_ErrorDestroySemaphoreFailed : EB_ErrorNone;
403
54.3k
    free(semaphore_handle);
404
54.3k
#endif
405
406
54.3k
    return return_error;
407
54.3k
}
408
409
/***************************************
410
 * svt_create_mutex
411
 ***************************************/
412
116k
EbHandle svt_create_mutex(void) {
413
116k
    EbHandle mutex_handle;
414
415
#ifdef _WIN32
416
    mutex_handle = (EbHandle)CreateMutex(NULL, // default security attributes
417
                                         false, // false := not initially owned
418
                                         NULL); // mutex is not named
419
420
#else
421
422
116k
    mutex_handle = (EbHandle)malloc(sizeof(pthread_mutex_t));
423
424
116k
    if (mutex_handle != NULL) {
425
116k
        pthread_mutex_init((pthread_mutex_t*)mutex_handle,
426
116k
                           NULL); // default attributes
427
116k
    }
428
116k
#endif
429
430
116k
    return mutex_handle;
431
116k
}
432
433
/***************************************
434
 * svt_release_mutex
435
 ***************************************/
436
236k
EbErrorType svt_release_mutex(EbHandle mutex_handle) {
437
236k
    EbErrorType return_error;
438
439
#ifdef _WIN32
440
    return_error = !ReleaseMutex((HANDLE)mutex_handle) ? EB_ErrorMutexUnresponsive : EB_ErrorNone;
441
#else
442
236k
    return_error = pthread_mutex_unlock((pthread_mutex_t*)mutex_handle) ? EB_ErrorMutexUnresponsive : EB_ErrorNone;
443
236k
#endif
444
445
236k
    return return_error;
446
236k
}
447
448
/***************************************
449
 * svt_block_on_mutex
450
 ***************************************/
451
236k
EbErrorType svt_block_on_mutex(EbHandle mutex_handle) {
452
236k
    EbErrorType return_error;
453
454
#ifdef _WIN32
455
    return_error = WaitForSingleObject((HANDLE)mutex_handle, INFINITE) ? EB_ErrorMutexUnresponsive : EB_ErrorNone;
456
#else
457
236k
    return_error = pthread_mutex_lock((pthread_mutex_t*)mutex_handle) ? EB_ErrorMutexUnresponsive : EB_ErrorNone;
458
236k
#endif
459
460
236k
    return return_error;
461
236k
}
462
463
/***************************************
464
 * svt_destroy_mutex
465
 ***************************************/
466
116k
EbErrorType svt_destroy_mutex(EbHandle mutex_handle) {
467
116k
    EbErrorType return_error;
468
469
#ifdef _WIN32
470
    return_error = !CloseHandle((HANDLE)mutex_handle) ? EB_ErrorDestroyMutexFailed : EB_ErrorNone;
471
#else
472
116k
    return_error = pthread_mutex_destroy((pthread_mutex_t*)mutex_handle) ? EB_ErrorDestroyMutexFailed : EB_ErrorNone;
473
116k
    free(mutex_handle);
474
116k
#endif
475
476
116k
    return return_error;
477
116k
}
478
479
/*
480
    set an atomic variable to an input value
481
*/
482
984
void svt_aom_atomic_set_u32(AtomicVarU32* var, uint32_t in) {
483
984
    svt_block_on_mutex(var->mutex);
484
984
    var->obj = in;
485
984
    svt_release_mutex(var->mutex);
486
984
}
487
488
/*
489
    create condition variable
490
491
    Condition variables are synchronization primitives that enable
492
    threads to wait until a particular condition occurs.
493
    Condition variables enable threads to atomically release
494
    a lock(mutex) and enter the sleeping state.
495
    it could be seen as a combined: wait and release mutex
496
*/
497
984
EbErrorType svt_create_cond_var(CondVar* cond_var) {
498
984
    EbErrorType return_error;
499
984
    cond_var->val = 0;
500
#ifdef _WIN32
501
    InitializeCriticalSection(&cond_var->cs);
502
    InitializeConditionVariable(&cond_var->cv);
503
    return_error = EB_ErrorNone;
504
#else
505
984
    pthread_mutex_init(&cond_var->m_mutex, NULL);
506
984
    return_error = pthread_cond_init(&cond_var->m_cond, NULL);
507
508
984
#endif
509
984
    return return_error;
510
984
}
511
512
/*
513
    set a  condition variable to the new value
514
*/
515
0
EbErrorType svt_set_cond_var(CondVar* cond_var, int32_t newval) {
516
0
    EbErrorType return_error;
517
#ifdef _WIN32
518
    EnterCriticalSection(&cond_var->cs);
519
    cond_var->val = newval;
520
    WakeAllConditionVariable(&cond_var->cv);
521
    LeaveCriticalSection(&cond_var->cs);
522
    return_error = EB_ErrorNone;
523
#else
524
0
    return_error  = pthread_mutex_lock(&cond_var->m_mutex);
525
0
    cond_var->val = newval;
526
0
    return_error |= pthread_cond_broadcast(&cond_var->m_cond);
527
0
    return_error |= pthread_mutex_unlock(&cond_var->m_mutex);
528
0
#endif
529
0
    return return_error;
530
0
}
531
532
/*
533
    wait until the cond variable changes to a value
534
    different than input
535
*/
536
537
0
EbErrorType svt_wait_cond_var(CondVar* cond_var, int32_t input) {
538
#ifdef _WIN32
539
540
    EnterCriticalSection(&cond_var->cs);
541
    while (cond_var->val == input) {
542
        SleepConditionVariableCS(&cond_var->cv, &cond_var->cs, INFINITE);
543
    }
544
    LeaveCriticalSection(&cond_var->cs);
545
#else
546
0
    if (pthread_mutex_lock(&cond_var->m_mutex)) {
547
0
        return EB_ErrorMutexUnresponsive;
548
0
    }
549
0
    while (cond_var->val == input) {
550
0
        if (pthread_cond_wait(&cond_var->m_cond, &cond_var->m_mutex)) {
551
0
            (void)pthread_mutex_unlock(&cond_var->m_mutex);
552
0
            return EB_ErrorMutexUnresponsive;
553
0
        }
554
0
    }
555
0
    if (pthread_mutex_unlock(&cond_var->m_mutex)) {
556
0
        return EB_ErrorMutexUnresponsive;
557
0
    }
558
0
#endif
559
0
    return EB_ErrorNone;
560
0
}
561
562
13.2k
void svt_run_once(OnceType* once_control, OnceFn init_routine) {
563
#ifdef _WIN32
564
    InitOnceExecuteOnce(once_control, init_routine, NULL, NULL);
565
#else
566
13.2k
    pthread_once(once_control, init_routine);
567
13.2k
#endif
568
13.2k
}