Coverage Report

Created: 2025-07-16 07:53

/src/openh264/codec/common/src/WelsThreadLib.cpp
Line
Count
Source (jump to first uncovered line)
1
/*!
2
 * \copy
3
 *     Copyright (c)  2009-2013, Cisco Systems
4
 *     All rights reserved.
5
 *
6
 *     Redistribution and use in source and binary forms, with or without
7
 *     modification, are permitted provided that the following conditions
8
 *     are met:
9
 *
10
 *        * Redistributions of source code must retain the above copyright
11
 *          notice, this list of conditions and the following disclaimer.
12
 *
13
 *        * Redistributions in binary form must reproduce the above copyright
14
 *          notice, this list of conditions and the following disclaimer in
15
 *          the documentation and/or other materials provided with the
16
 *          distribution.
17
 *
18
 *     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
 *     "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20
 *     LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21
 *     FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22
 *     COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23
 *     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24
 *     BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25
 *     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
 *     CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27
 *     LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28
 *     ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29
 *     POSSIBILITY OF SUCH DAMAGE.
30
 *
31
 *
32
 * \file    WelsThreadLib.c
33
 *
34
 * \brief   Interfaces introduced in thread programming
35
 *
36
 * \date    11/17/2009 Created
37
 *
38
 *************************************************************************************
39
 */
40
41
42
#if defined(__linux__) || defined(__GNU__)
43
#ifndef _GNU_SOURCE
44
#define _GNU_SOURCE
45
#endif
46
#include <sched.h>
47
#elif !defined(_WIN32) && !defined(__CYGWIN__)
48
#include <sys/types.h>
49
#include <sys/param.h>
50
#include <unistd.h>
51
#ifdef __EMSCRIPTEN__
52
#include <emscripten/threading.h>
53
#elif !defined(__Fuchsia__)
54
#include <sys/sysctl.h>
55
#endif
56
#ifdef __APPLE__
57
#define HW_NCPU_NAME "hw.logicalcpu"
58
#elif defined(HW_NCPUONLINE)
59
#define HW_NCPU_NAME "hw.ncpuonline"
60
#else
61
#define HW_NCPU_NAME "hw.ncpu"
62
#endif
63
#endif
64
#ifdef ANDROID_NDK
65
#include <cpu-features.h>
66
#endif
67
#ifdef __ANDROID__
68
#include <android/api-level.h>
69
#endif
70
71
#include "WelsThreadLib.h"
72
#include <stdio.h>
73
#include <stdlib.h>
74
75
76
#if defined(_WIN32) || defined(__CYGWIN__)
77
78
WELS_THREAD_ERROR_CODE    WelsMutexInit (WELS_MUTEX*    mutex) {
79
  InitializeCriticalSection (mutex);
80
81
  return WELS_THREAD_ERROR_OK;
82
}
83
84
WELS_THREAD_ERROR_CODE    WelsMutexLock (WELS_MUTEX*    mutex) {
85
  EnterCriticalSection (mutex);
86
87
  return WELS_THREAD_ERROR_OK;
88
}
89
90
WELS_THREAD_ERROR_CODE    WelsMutexUnlock (WELS_MUTEX* mutex) {
91
  LeaveCriticalSection (mutex);
92
93
  return WELS_THREAD_ERROR_OK;
94
}
95
96
WELS_THREAD_ERROR_CODE    WelsMutexDestroy (WELS_MUTEX* mutex) {
97
  DeleteCriticalSection (mutex);
98
99
  return WELS_THREAD_ERROR_OK;
100
}
101
102
#else /* _WIN32 */
103
104
0
WELS_THREAD_ERROR_CODE    WelsMutexInit (WELS_MUTEX*    mutex) {
105
0
  return pthread_mutex_init (mutex, NULL);
106
0
}
107
108
0
WELS_THREAD_ERROR_CODE    WelsMutexLock (WELS_MUTEX*    mutex) {
109
0
  return pthread_mutex_lock (mutex);
110
0
}
111
112
0
WELS_THREAD_ERROR_CODE    WelsMutexUnlock (WELS_MUTEX* mutex) {
113
0
  return pthread_mutex_unlock (mutex);
114
0
}
115
116
0
WELS_THREAD_ERROR_CODE    WelsMutexDestroy (WELS_MUTEX* mutex) {
117
0
  return pthread_mutex_destroy (mutex);
118
0
}
119
120
#endif /* !_WIN32 */
121
122
#if defined(_WIN32) || defined(__CYGWIN__)
123
124
WELS_THREAD_ERROR_CODE    WelsEventOpen (WELS_EVENT* event, const char* event_name) {
125
  WELS_EVENT   h = CreateEvent (NULL, FALSE, FALSE, NULL);
126
  *event = h;
127
  if (h == NULL) {
128
    return WELS_THREAD_ERROR_GENERAL;
129
  }
130
  return WELS_THREAD_ERROR_OK;
131
}
132
133
WELS_THREAD_ERROR_CODE    WelsEventSignal (WELS_EVENT* event, WELS_MUTEX *pMutex, int* iCondition) {
134
  (*iCondition) --;
135
  if ((*iCondition) <= 0) {
136
    if (SetEvent (*event)) {
137
      return WELS_THREAD_ERROR_OK;
138
    }
139
  }
140
  return WELS_THREAD_ERROR_GENERAL;
141
}
142
143
WELS_THREAD_ERROR_CODE    WelsEventWait (WELS_EVENT* event, WELS_MUTEX* pMutex, int& iCondition) {
144
  return WaitForSingleObject (*event, INFINITE);
145
}
146
147
WELS_THREAD_ERROR_CODE    WelsEventWaitWithTimeOut (WELS_EVENT* event, uint32_t dwMilliseconds, WELS_MUTEX* pMutex) {
148
  return WaitForSingleObject (*event, dwMilliseconds);
149
}
150
151
WELS_THREAD_ERROR_CODE    WelsMultipleEventsWaitSingleBlocking (uint32_t nCount,
152
    WELS_EVENT* event_list, WELS_EVENT* master_even, WELS_MUTEX* pMutext) {
153
  // Don't need/use the master event for anything, since windows has got WaitForMultipleObjects
154
  return WaitForMultipleObjects (nCount, event_list, FALSE, INFINITE);
155
}
156
157
WELS_THREAD_ERROR_CODE    WelsEventClose (WELS_EVENT* event, const char* event_name) {
158
  CloseHandle (*event);
159
160
  *event = NULL;
161
  return WELS_THREAD_ERROR_OK;
162
}
163
164
#ifndef WP80
165
void WelsSleep (uint32_t dwMilliSecond) {
166
  ::Sleep (dwMilliSecond);
167
}
168
#else
169
void WelsSleep (uint32_t dwMilliSecond) {
170
  static WELS_EVENT hSleepEvent = NULL;
171
  if (!hSleepEvent) {
172
    WELS_EVENT hLocalSleepEvent = NULL;
173
    WELS_THREAD_ERROR_CODE ret = WelsEventOpen (&hLocalSleepEvent);
174
    if (WELS_THREAD_ERROR_OK != ret) {
175
      return;
176
    }
177
    WELS_EVENT hPreviousEvent = InterlockedCompareExchangePointerRelease (&hSleepEvent, hLocalSleepEvent, NULL);
178
    if (hPreviousEvent) {
179
      WelsEventClose (&hLocalSleepEvent);
180
    }
181
    //On this singleton usage idea of using InterlockedCompareExchangePointerRelease:
182
    //   similar idea of can be found at msdn blog when introducing InterlockedCompareExchangePointerRelease
183
  }
184
185
  WaitForSingleObject (hSleepEvent, dwMilliSecond);
186
}
187
#endif
188
189
WELS_THREAD_ERROR_CODE    WelsThreadCreate (WELS_THREAD_HANDLE* thread,  LPWELS_THREAD_ROUTINE  routine,
190
    void* arg, WELS_THREAD_ATTR attr) {
191
  WELS_THREAD_HANDLE   h = CreateThread (NULL, 0, routine, arg, 0, NULL);
192
193
  if (h == NULL) {
194
    return WELS_THREAD_ERROR_GENERAL;
195
  }
196
  * thread = h;
197
198
  return WELS_THREAD_ERROR_OK;
199
}
200
201
WELS_THREAD_ERROR_CODE WelsThreadSetName (const char* thread_name) {
202
  // do nothing
203
  return WELS_THREAD_ERROR_OK;
204
}
205
206
207
WELS_THREAD_ERROR_CODE    WelsThreadJoin (WELS_THREAD_HANDLE  thread) {
208
  WaitForSingleObject (thread, INFINITE);
209
  CloseHandle (thread);
210
211
  return WELS_THREAD_ERROR_OK;
212
}
213
214
215
WELS_THREAD_HANDLE        WelsThreadSelf() {
216
  return GetCurrentThread();
217
}
218
219
WELS_THREAD_ERROR_CODE    WelsQueryLogicalProcessInfo (WelsLogicalProcessInfo* pInfo) {
220
  SYSTEM_INFO  si;
221
222
  GetSystemInfo (&si);
223
224
  pInfo->ProcessorCount = si.dwNumberOfProcessors;
225
226
  return WELS_THREAD_ERROR_OK;
227
}
228
229
#else //platform: #ifdef _WIN32
230
231
WELS_THREAD_ERROR_CODE    WelsThreadCreate (WELS_THREAD_HANDLE* thread,  LPWELS_THREAD_ROUTINE  routine,
232
0
    void* arg, WELS_THREAD_ATTR attr) {
233
0
  WELS_THREAD_ERROR_CODE err = 0;
234
235
0
  pthread_attr_t at;
236
0
  err = pthread_attr_init (&at);
237
0
  if (err)
238
0
    return err;
239
0
#if !defined(__ANDROID__) && !defined(__Fuchsia__)
240
0
  err = pthread_attr_setscope (&at, PTHREAD_SCOPE_SYSTEM);
241
0
  if (err)
242
0
    return err;
243
0
  err = pthread_attr_setschedpolicy (&at, SCHED_FIFO);
244
0
  if (err)
245
0
    return err;
246
0
#endif
247
0
  err = pthread_create (thread, &at, routine, arg);
248
249
0
  pthread_attr_destroy (&at);
250
251
0
  return err;
252
0
}
253
254
0
WELS_THREAD_ERROR_CODE WelsThreadSetName (const char* thread_name) {
255
#ifdef APPLE_IOS
256
  pthread_setname_np (thread_name);
257
#endif
258
#if defined(__ANDROID__) && __ANDROID_API__ >= 9
259
  pthread_setname_np (pthread_self(), thread_name);
260
#endif
261
  // do nothing
262
0
  return WELS_THREAD_ERROR_OK;
263
0
}
264
265
0
WELS_THREAD_ERROR_CODE    WelsThreadJoin (WELS_THREAD_HANDLE  thread) {
266
0
  return pthread_join (thread, NULL);
267
0
}
268
269
0
WELS_THREAD_HANDLE        WelsThreadSelf() {
270
0
  return pthread_self();
271
0
}
272
273
// unnamed semaphores aren't supported on OS X
274
275
0
WELS_THREAD_ERROR_CODE    WelsEventOpen (WELS_EVENT* p_event, const char* event_name) {
276
#ifdef __APPLE__
277
  WELS_THREAD_ERROR_CODE err= pthread_cond_init (p_event, NULL);
278
  return err;
279
#else
280
0
  WELS_EVENT event = (WELS_EVENT) malloc (sizeof (*event));
281
0
  if (event == NULL){
282
0
    *p_event = NULL;
283
0
    return WELS_THREAD_ERROR_GENERAL;
284
0
  }
285
0
  WELS_THREAD_ERROR_CODE err = sem_init (event, 0, 0);
286
0
  if (!err) {
287
0
    *p_event = event;
288
0
    return err;
289
0
  }
290
0
  free (event);
291
0
  *p_event = NULL;
292
0
  return err;
293
0
#endif
294
0
}
295
0
WELS_THREAD_ERROR_CODE    WelsEventClose (WELS_EVENT* event, const char* event_name) {
296
  //printf("event_close:%x, %s\n", event, event_name);
297
#ifdef __APPLE__
298
  WELS_THREAD_ERROR_CODE err = pthread_cond_destroy (event);
299
  return err;
300
#else
301
0
  WELS_THREAD_ERROR_CODE err = sem_destroy (*event); // match with sem_init
302
0
  free (*event);
303
0
  *event = NULL;
304
0
  return err;
305
0
#endif
306
0
}
307
308
0
void WelsSleep (uint32_t dwMilliSecond) {
309
0
  usleep (dwMilliSecond * 1000);
310
0
}
311
312
0
WELS_THREAD_ERROR_CODE   WelsEventSignal (WELS_EVENT* event, WELS_MUTEX *pMutex, int* iCondition) {
313
0
  WELS_THREAD_ERROR_CODE err = 0;
314
  //fprintf( stderr, "before signal it, event=%x iCondition= %d..\n", event, *iCondition );
315
#ifdef __APPLE__
316
  WelsMutexLock (pMutex);
317
  (*iCondition) --;
318
  WelsMutexUnlock (pMutex);
319
  if ((*iCondition) <= 0) {
320
  err = pthread_cond_signal (event);
321
  //fprintf( stderr, "signal it, event=%x iCondition= %d..\n",event, *iCondition );
322
323
  }
324
#else
325
0
    (*iCondition) --;
326
0
    if ((*iCondition) <= 0) {
327
//  int32_t val = 0;
328
//  sem_getvalue(event, &val);
329
//  fprintf( stderr, "before signal it, val= %d..\n",val );
330
0
  if (event != NULL)
331
0
    err = sem_post (*event);
332
//  sem_getvalue(event, &val);
333
    //fprintf( stderr, "signal it, event=%x iCondition= %d..\n",event, *iCondition );
334
0
    }
335
0
#endif
336
  //fprintf( stderr, "after signal it, event=%x  iCondition= %d..\n",event, *iCondition );
337
0
  return err;
338
0
}
339
340
0
WELS_THREAD_ERROR_CODE WelsEventWait (WELS_EVENT* event, WELS_MUTEX* pMutex, int& iCondition) {
341
#ifdef __APPLE__
342
  int err = 0;
343
  WelsMutexLock(pMutex);
344
  //fprintf( stderr, "WelsEventWait event %x %d..\n", event, iCondition );
345
  while (iCondition>0) {
346
    err = pthread_cond_wait (event, pMutex);
347
  }
348
  WelsMutexUnlock(pMutex);
349
  return err;
350
#else
351
0
  return sem_wait (*event); // blocking until signaled
352
0
#endif
353
0
}
354
355
0
WELS_THREAD_ERROR_CODE    WelsEventWaitWithTimeOut (WELS_EVENT* event, uint32_t dwMilliseconds, WELS_MUTEX* pMutex) {
356
357
0
  if (dwMilliseconds != (uint32_t) - 1) {
358
#if defined(__APPLE__)
359
    return pthread_cond_wait (event, pMutex);
360
#else
361
0
    return sem_wait (*event);
362
0
#endif
363
0
  } else {
364
0
    struct timespec ts;
365
0
    struct timeval tv;
366
367
0
    gettimeofday (&tv, 0);
368
369
0
    ts.tv_nsec = tv.tv_usec * 1000 + dwMilliseconds * 1000000;
370
0
    ts.tv_sec = tv.tv_sec + ts.tv_nsec / 1000000000;
371
0
    ts.tv_nsec %= 1000000000;
372
373
#if defined(__APPLE__)
374
    return pthread_cond_timedwait (event, pMutex, &ts);
375
#else
376
0
    return sem_timedwait (*event, &ts);
377
0
#endif
378
0
  }
379
380
0
}
381
382
WELS_THREAD_ERROR_CODE    WelsMultipleEventsWaitSingleBlocking (uint32_t nCount,
383
0
    WELS_EVENT* event_list, WELS_EVENT* master_event, WELS_MUTEX* pMutex) {
384
0
  uint32_t nIdx = 0;
385
0
  uint32_t uiAccessTime = 2; // 2 us once
386
387
0
  if (nCount == 0)
388
0
    return WELS_THREAD_ERROR_WAIT_FAILED;
389
#if defined(__APPLE__)
390
  if (master_event != NULL) {
391
    // This design relies on the events actually being semaphores;
392
    // if multiple events in the list have been signalled, the master
393
    // event should have a similar count (events in windows can't keep
394
    // track of the actual count, but the master event isn't needed there
395
    // since it uses WaitForMultipleObjects).
396
    int32_t err = pthread_cond_wait (master_event, pMutex);
397
    if (err != WELS_THREAD_ERROR_OK)
398
      return err;
399
    uiAccessTime = 0; // no blocking, just quickly loop through all to find the one that was signalled
400
  }
401
402
  while (1) {
403
    nIdx = 0; // access each event by order
404
    while (nIdx < nCount) {
405
      int32_t err = 0;
406
      int32_t wait_count = 0;
407
408
      /*
409
       * although such interface is not used in __GNUC__ like platform, to use
410
       * pthread_cond_timedwait() might be better choice if need
411
       */
412
      do {
413
        err = pthread_cond_wait (&event_list[nIdx], pMutex);
414
        if (WELS_THREAD_ERROR_OK == err)
415
          return WELS_THREAD_ERROR_WAIT_OBJECT_0 + nIdx;
416
        else if (wait_count > 0 || uiAccessTime == 0)
417
          break;
418
        usleep (uiAccessTime);
419
        ++ wait_count;
420
      } while (1);
421
      // we do need access next event next time
422
      ++ nIdx;
423
    }
424
    usleep (1); // switch to working threads
425
    if (master_event != NULL) {
426
      // A master event was used and was signalled, but none of the events in the
427
      // list was found to be signalled, thus wait a little more when rechecking
428
      // the list to avoid busylooping here.
429
      // If we ever hit this codepath it's mostly a bug in the code that signals
430
      // the events.
431
      uiAccessTime = 2;
432
    }
433
  }
434
#else
435
0
  if (master_event != NULL) {
436
    // This design relies on the events actually being semaphores;
437
    // if multiple events in the list have been signalled, the master
438
    // event should have a similar count (events in windows can't keep
439
    // track of the actual count, but the master event isn't needed there
440
    // since it uses WaitForMultipleObjects).
441
0
    int32_t err = sem_wait (*master_event);
442
0
    if (err != WELS_THREAD_ERROR_OK)
443
0
      return err;
444
0
    uiAccessTime = 0; // no blocking, just quickly loop through all to find the one that was signalled
445
0
  }
446
447
0
  while (1) {
448
0
    nIdx = 0; // access each event by order
449
0
    while (nIdx < nCount) {
450
0
      int32_t err = 0;
451
0
      int32_t wait_count = 0;
452
453
      /*
454
       * although such interface is not used in __GNUC__ like platform, to use
455
       * pthread_cond_timedwait() might be better choice if need
456
       */
457
0
      do {
458
0
        err = sem_trywait (event_list[nIdx]);
459
0
        if (WELS_THREAD_ERROR_OK == err)
460
0
          return WELS_THREAD_ERROR_WAIT_OBJECT_0 + nIdx;
461
0
        else if (wait_count > 0 || uiAccessTime == 0)
462
0
          break;
463
0
        usleep (uiAccessTime);
464
0
        ++ wait_count;
465
0
      } while (1);
466
      // we do need access next event next time
467
0
      ++ nIdx;
468
0
    }
469
0
    usleep (1); // switch to working threads
470
0
    if (master_event != NULL) {
471
      // A master event was used and was signalled, but none of the events in the
472
      // list was found to be signalled, thus wait a little more when rechecking
473
      // the list to avoid busylooping here.
474
      // If we ever hit this codepath it's mostly a bug in the code that signals
475
      // the events.
476
0
      uiAccessTime = 2;
477
0
    }
478
0
  }
479
480
0
#endif
481
0
  return WELS_THREAD_ERROR_WAIT_FAILED;
482
0
}
483
484
0
WELS_THREAD_ERROR_CODE    WelsQueryLogicalProcessInfo (WelsLogicalProcessInfo* pInfo) {
485
#ifdef ANDROID_NDK
486
  pInfo->ProcessorCount = android_getCpuCount();
487
  return WELS_THREAD_ERROR_OK;
488
#elif defined(__linux__) || defined(__GNU__)
489
490
0
  cpu_set_t cpuset;
491
492
0
  CPU_ZERO (&cpuset);
493
494
0
  if (!sched_getaffinity (0, sizeof (cpuset), &cpuset)) {
495
0
#ifdef CPU_COUNT
496
0
    pInfo->ProcessorCount = CPU_COUNT (&cpuset);
497
#else
498
    int32_t count = 0;
499
    for (int i = 0; i < CPU_SETSIZE; i++) {
500
      if (CPU_ISSET (i, &cpuset)) {
501
        count++;
502
      }
503
    }
504
    pInfo->ProcessorCount = count;
505
#endif
506
0
  } else {
507
0
    pInfo->ProcessorCount = 1;
508
0
  }
509
510
0
  return WELS_THREAD_ERROR_OK;
511
512
#elif defined(__EMSCRIPTEN__)
513
514
  // There is not yet a way to determine CPU count in emscripten JS environment.
515
  pInfo->ProcessorCount = emscripten_num_logical_cores();
516
  return WELS_THREAD_ERROR_OK;
517
518
#elif defined(__Fuchsia__)
519
520
  pInfo->ProcessorCount = sysconf(_SC_NPROCESSORS_ONLN);
521
  return WELS_THREAD_ERROR_OK;
522
#else
523
524
  size_t len = sizeof (pInfo->ProcessorCount);
525
526
#if defined(__OpenBSD__)
527
  int scname[] = { CTL_HW, HW_NCPUONLINE };
528
  if (sysctl (scname, 2, &pInfo->ProcessorCount, &len, NULL, 0) == -1)
529
#else
530
  if (sysctlbyname (HW_NCPU_NAME, &pInfo->ProcessorCount, &len, NULL, 0) == -1)
531
#endif
532
    pInfo->ProcessorCount = 1;
533
534
  return WELS_THREAD_ERROR_OK;
535
536
#endif//__linux__
537
0
}
538
539
#endif