Coverage Report

Created: 2026-07-30 06:27

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.7k
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.7k
}
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.7k
static void* svt_thread_trampoline(void* p) {
104
12.7k
    SvtThreadStart* payload = (SvtThreadStart*)p;
105
12.7k
    void* (*fn)(void*)      = payload->fn;
106
12.7k
    void* arg               = payload->arg;
107
12.7k
    char  name[16];
108
12.7k
    strncpy(name, payload->name, sizeof(name) - 1);
109
12.7k
    name[sizeof(name) - 1] = '\0';
110
12.7k
    free(payload);
111
112
12.7k
    if (name[0]) {
113
12.7k
        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.7k
    }
120
121
12.7k
    return fn(arg);
122
12.7k
}
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.80k
void svt_format_thread_name(char* buf, size_t size, const char* prefix, uint32_t index) {
175
9.80k
    snprintf(buf, size, "%s%u", prefix, index);
176
9.80k
}
177
178
/****************************************
179
 * svt_create_thread
180
 ****************************************/
181
12.7k
EbHandle svt_create_thread(void* thread_function(void*), void* thread_context, const char* name) {
182
12.7k
    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.7k
    if (name && !strncmp(name, "svt_aom_", 8)) {
190
2.91k
        name += 8;
191
2.91k
    }
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.7k
    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.7k
    pthread_attr_t attr;
234
12.7k
    if (pthread_attr_init(&attr)) {
235
0
        SVT_ERROR("Failed to initialize thread attributes\n");
236
0
        return NULL;
237
0
    }
238
239
12.7k
    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.7k
    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.7k
    (void)pthread_attr_setstacksize(&attr, min_stack_size);
251
252
12.7k
    pthread_t* th = malloc(sizeof(*th));
253
12.7k
    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.7k
    SvtThreadStart* payload = malloc(sizeof(*payload));
260
12.7k
    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.7k
    payload->fn  = thread_function;
267
12.7k
    payload->arg = thread_context;
268
12.7k
    if (name && *name) {
269
12.7k
        strncpy(payload->name, name, sizeof(payload->name) - 1);
270
12.7k
        payload->name[sizeof(payload->name) - 1] = '\0';
271
12.7k
    } else {
272
0
        payload->name[0] = '\0';
273
0
    }
274
275
12.7k
    int ret;
276
12.7k
    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.7k
    pthread_attr_destroy(&attr);
285
286
12.7k
    thread_handle = th;
287
12.7k
#endif // _WIN32
288
289
12.7k
    return thread_handle;
290
12.7k
}
291
292
/****************************************
293
 * svt_destroy_thread
294
 ****************************************/
295
12.7k
EbErrorType svt_destroy_thread(EbHandle thread_handle) {
296
12.7k
    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.7k
    error_return = pthread_join(*((pthread_t*)thread_handle), NULL) ? EB_ErrorDestroyThreadFailed : EB_ErrorNone;
303
12.7k
    free(thread_handle);
304
12.7k
#endif // _WIN32
305
306
12.7k
    return error_return;
307
12.7k
}
308
309
/***************************************
310
 * svt_create_semaphore
311
 ***************************************/
312
53.7k
EbHandle svt_create_semaphore(uint32_t initial_count, uint32_t max_count) {
313
53.7k
    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
    semaphore_handle = (EbHandle)dispatch_semaphore_create(initial_count);
323
#else
324
53.7k
    UNUSED(max_count);
325
326
53.7k
    semaphore_handle = (sem_t*)malloc(sizeof(sem_t));
327
53.7k
    if (semaphore_handle != NULL) {
328
53.7k
        sem_init((sem_t*)semaphore_handle, // semaphore handle
329
53.7k
                 0, // shared semaphore (not local)
330
53.7k
                 initial_count); // initial count
331
53.7k
    }
332
53.7k
#endif
333
334
53.7k
    return semaphore_handle;
335
53.7k
}
336
337
/***************************************
338
 * svt_post_semaphore
339
 ***************************************/
340
49.0k
EbErrorType svt_post_semaphore(EbHandle semaphore_handle) {
341
49.0k
    EbErrorType return_error;
342
343
#ifdef _WIN32
344
    return_error = !ReleaseSemaphore(semaphore_handle, // semaphore handle
345
                                     1, // amount to increment the semaphore
346
                                     NULL) // pointer to previous count (optional)
347
        ? EB_ErrorSemaphoreUnresponsive
348
        : EB_ErrorNone;
349
#elif defined(__APPLE__)
350
    dispatch_semaphore_signal((dispatch_semaphore_t)semaphore_handle);
351
    return_error = EB_ErrorNone;
352
#else
353
49.0k
    return_error = sem_post((sem_t*)semaphore_handle) ? EB_ErrorSemaphoreUnresponsive : EB_ErrorNone;
354
49.0k
#endif
355
356
49.0k
    return return_error;
357
49.0k
}
358
359
/***************************************
360
 * svt_block_on_semaphore
361
 ***************************************/
362
49.0k
EbErrorType svt_block_on_semaphore(EbHandle semaphore_handle) {
363
49.0k
    EbErrorType return_error;
364
365
#ifdef _WIN32
366
    return_error = WaitForSingleObject((HANDLE)semaphore_handle, INFINITE) ? EB_ErrorSemaphoreUnresponsive
367
                                                                           : EB_ErrorNone;
368
#elif defined(__APPLE__)
369
    return_error = dispatch_semaphore_wait((dispatch_semaphore_t)semaphore_handle, DISPATCH_TIME_FOREVER)
370
        ? EB_ErrorSemaphoreUnresponsive
371
        : EB_ErrorNone;
372
#else
373
49.0k
    int ret;
374
49.0k
    do {
375
49.0k
        ret = sem_wait((sem_t*)semaphore_handle);
376
49.0k
    } while (ret == -1 && errno == EINTR);
377
49.0k
    return_error = ret ? EB_ErrorSemaphoreUnresponsive : EB_ErrorNone;
378
49.0k
#endif
379
380
49.0k
    return return_error;
381
49.0k
}
382
383
/***************************************
384
 * svt_destroy_semaphore
385
 ***************************************/
386
53.7k
EbErrorType svt_destroy_semaphore(EbHandle semaphore_handle) {
387
53.7k
    EbErrorType return_error;
388
389
#ifdef _WIN32
390
    return_error = !CloseHandle((HANDLE)semaphore_handle) ? EB_ErrorDestroySemaphoreFailed : EB_ErrorNone;
391
#elif defined(__APPLE__)
392
    dispatch_release((dispatch_semaphore_t)semaphore_handle);
393
    return_error = EB_ErrorNone;
394
#else
395
53.7k
    return_error = sem_destroy((sem_t*)semaphore_handle) ? EB_ErrorDestroySemaphoreFailed : EB_ErrorNone;
396
53.7k
    free(semaphore_handle);
397
53.7k
#endif
398
399
53.7k
    return return_error;
400
53.7k
}
401
402
/***************************************
403
 * svt_create_mutex
404
 ***************************************/
405
116k
EbHandle svt_create_mutex(void) {
406
116k
    EbHandle mutex_handle;
407
408
#ifdef _WIN32
409
    mutex_handle = (EbHandle)CreateMutex(NULL, // default security attributes
410
                                         false, // false := not initially owned
411
                                         NULL); // mutex is not named
412
413
#else
414
415
116k
    mutex_handle = (EbHandle)malloc(sizeof(pthread_mutex_t));
416
417
116k
    if (mutex_handle != NULL) {
418
116k
        pthread_mutex_init((pthread_mutex_t*)mutex_handle,
419
116k
                           NULL); // default attributes
420
116k
    }
421
116k
#endif
422
423
116k
    return mutex_handle;
424
116k
}
425
426
/***************************************
427
 * svt_release_mutex
428
 ***************************************/
429
236k
EbErrorType svt_release_mutex(EbHandle mutex_handle) {
430
236k
    EbErrorType return_error;
431
432
#ifdef _WIN32
433
    return_error = !ReleaseMutex((HANDLE)mutex_handle) ? EB_ErrorMutexUnresponsive : EB_ErrorNone;
434
#else
435
236k
    return_error = pthread_mutex_unlock((pthread_mutex_t*)mutex_handle) ? EB_ErrorMutexUnresponsive : EB_ErrorNone;
436
236k
#endif
437
438
236k
    return return_error;
439
236k
}
440
441
/***************************************
442
 * svt_block_on_mutex
443
 ***************************************/
444
236k
EbErrorType svt_block_on_mutex(EbHandle mutex_handle) {
445
236k
    EbErrorType return_error;
446
447
#ifdef _WIN32
448
    return_error = WaitForSingleObject((HANDLE)mutex_handle, INFINITE) ? EB_ErrorMutexUnresponsive : EB_ErrorNone;
449
#else
450
236k
    return_error = pthread_mutex_lock((pthread_mutex_t*)mutex_handle) ? EB_ErrorMutexUnresponsive : EB_ErrorNone;
451
236k
#endif
452
453
236k
    return return_error;
454
236k
}
455
456
/***************************************
457
 * svt_destroy_mutex
458
 ***************************************/
459
116k
EbErrorType svt_destroy_mutex(EbHandle mutex_handle) {
460
116k
    EbErrorType return_error;
461
462
#ifdef _WIN32
463
    return_error = CloseHandle((HANDLE)mutex_handle) ? EB_ErrorDestroyMutexFailed : EB_ErrorNone;
464
#else
465
116k
    return_error = pthread_mutex_destroy((pthread_mutex_t*)mutex_handle) ? EB_ErrorDestroyMutexFailed : EB_ErrorNone;
466
116k
    free(mutex_handle);
467
116k
#endif
468
469
116k
    return return_error;
470
116k
}
471
472
/*
473
    set an atomic variable to an input value
474
*/
475
970
void svt_aom_atomic_set_u32(AtomicVarU32* var, uint32_t in) {
476
970
    svt_block_on_mutex(var->mutex);
477
970
    var->obj = in;
478
970
    svt_release_mutex(var->mutex);
479
970
}
480
481
/*
482
    create condition variable
483
484
    Condition variables are synchronization primitives that enable
485
    threads to wait until a particular condition occurs.
486
    Condition variables enable threads to atomically release
487
    a lock(mutex) and enter the sleeping state.
488
    it could be seen as a combined: wait and release mutex
489
*/
490
970
EbErrorType svt_create_cond_var(CondVar* cond_var) {
491
970
    EbErrorType return_error;
492
970
    cond_var->val = 0;
493
#ifdef _WIN32
494
    InitializeCriticalSection(&cond_var->cs);
495
    InitializeConditionVariable(&cond_var->cv);
496
    return_error = EB_ErrorNone;
497
#else
498
970
    pthread_mutex_init(&cond_var->m_mutex, NULL);
499
970
    return_error = pthread_cond_init(&cond_var->m_cond, NULL);
500
501
970
#endif
502
970
    return return_error;
503
970
}
504
505
/*
506
    set a  condition variable to the new value
507
*/
508
0
EbErrorType svt_set_cond_var(CondVar* cond_var, int32_t newval) {
509
0
    EbErrorType return_error;
510
#ifdef _WIN32
511
    EnterCriticalSection(&cond_var->cs);
512
    cond_var->val = newval;
513
    WakeAllConditionVariable(&cond_var->cv);
514
    LeaveCriticalSection(&cond_var->cs);
515
    return_error = EB_ErrorNone;
516
#else
517
0
    return_error  = pthread_mutex_lock(&cond_var->m_mutex);
518
0
    cond_var->val = newval;
519
0
    return_error |= pthread_cond_broadcast(&cond_var->m_cond);
520
0
    return_error |= pthread_mutex_unlock(&cond_var->m_mutex);
521
0
#endif
522
0
    return return_error;
523
0
}
524
525
/*
526
    wait until the cond variable changes to a value
527
    different than input
528
*/
529
530
0
EbErrorType svt_wait_cond_var(CondVar* cond_var, int32_t input) {
531
#ifdef _WIN32
532
533
    EnterCriticalSection(&cond_var->cs);
534
    while (cond_var->val == input) {
535
        SleepConditionVariableCS(&cond_var->cv, &cond_var->cs, INFINITE);
536
    }
537
    LeaveCriticalSection(&cond_var->cs);
538
#else
539
0
    if (pthread_mutex_lock(&cond_var->m_mutex)) {
540
0
        return EB_ErrorMutexUnresponsive;
541
0
    }
542
0
    while (cond_var->val == input) {
543
0
        if (pthread_cond_wait(&cond_var->m_cond, &cond_var->m_mutex)) {
544
0
            (void)pthread_mutex_unlock(&cond_var->m_mutex);
545
0
            return EB_ErrorMutexUnresponsive;
546
0
        }
547
0
    }
548
0
    if (pthread_mutex_unlock(&cond_var->m_mutex)) {
549
0
        return EB_ErrorMutexUnresponsive;
550
0
    }
551
0
#endif
552
0
    return EB_ErrorNone;
553
0
}
554
555
13.0k
void svt_run_once(OnceType* once_control, OnceFn init_routine) {
556
#ifdef _WIN32
557
    InitOnceExecuteOnce(once_control, init_routine, NULL, NULL);
558
#else
559
13.0k
    pthread_once(once_control, init_routine);
560
13.0k
#endif
561
13.0k
}