/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, ¶m))) { |
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, ¶m))) { |
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, ¶m); |
232 | 0 | param.sched_priority = 99; |
233 | 0 | pthread_attr_setschedparam(&attr, ¶m); |
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 | } |