| /*! |
| * \copy |
| * Copyright (c) 2009-2013, Cisco Systems |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * * Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
| * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
| * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
| * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
| * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
| * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGE. |
| * |
| * |
| * \file WelsThreadLib.c |
| * |
| * \brief Interfaces introduced in thread programming |
| * |
| * \date 11/17/2009 Created |
| * |
| ************************************************************************************* |
| */ |
| |
| |
| #if defined(__linux__) || defined(__GNU__) |
| #ifndef _GNU_SOURCE |
| #define _GNU_SOURCE |
| #endif |
| #include <sched.h> |
| #elif !defined(_WIN32) && !defined(__CYGWIN__) |
| #include <sys/types.h> |
| #include <sys/param.h> |
| #include <unistd.h> |
| #ifdef __EMSCRIPTEN__ |
| #include <emscripten/threading.h> |
| #elif !defined(__Fuchsia__) |
| #include <sys/sysctl.h> |
| #endif |
| #ifdef __APPLE__ |
| #define HW_NCPU_NAME "hw.logicalcpu" |
| #elif defined(HW_NCPUONLINE) |
| #define HW_NCPU_NAME "hw.ncpuonline" |
| #else |
| #define HW_NCPU_NAME "hw.ncpu" |
| #endif |
| #endif |
| #ifdef ANDROID_NDK |
| #include <cpu-features.h> |
| #endif |
| #ifdef __ANDROID__ |
| #include <android/api-level.h> |
| #endif |
| |
| #include "WelsThreadLib.h" |
| #include <stdio.h> |
| #include <stdlib.h> |
| |
| |
| #if defined(_WIN32) || defined(__CYGWIN__) |
| |
| WELS_THREAD_ERROR_CODE WelsMutexInit (WELS_MUTEX* mutex) { |
| InitializeCriticalSection (mutex); |
| |
| return WELS_THREAD_ERROR_OK; |
| } |
| |
| WELS_THREAD_ERROR_CODE WelsMutexLock (WELS_MUTEX* mutex) { |
| EnterCriticalSection (mutex); |
| |
| return WELS_THREAD_ERROR_OK; |
| } |
| |
| WELS_THREAD_ERROR_CODE WelsMutexUnlock (WELS_MUTEX* mutex) { |
| LeaveCriticalSection (mutex); |
| |
| return WELS_THREAD_ERROR_OK; |
| } |
| |
| WELS_THREAD_ERROR_CODE WelsMutexDestroy (WELS_MUTEX* mutex) { |
| DeleteCriticalSection (mutex); |
| |
| return WELS_THREAD_ERROR_OK; |
| } |
| |
| #else /* _WIN32 */ |
| |
| WELS_THREAD_ERROR_CODE WelsMutexInit (WELS_MUTEX* mutex) { |
| return pthread_mutex_init (mutex, NULL); |
| } |
| |
| WELS_THREAD_ERROR_CODE WelsMutexLock (WELS_MUTEX* mutex) { |
| return pthread_mutex_lock (mutex); |
| } |
| |
| WELS_THREAD_ERROR_CODE WelsMutexUnlock (WELS_MUTEX* mutex) { |
| return pthread_mutex_unlock (mutex); |
| } |
| |
| WELS_THREAD_ERROR_CODE WelsMutexDestroy (WELS_MUTEX* mutex) { |
| return pthread_mutex_destroy (mutex); |
| } |
| |
| #endif /* !_WIN32 */ |
| |
| #if defined(_WIN32) || defined(__CYGWIN__) |
| |
| WELS_THREAD_ERROR_CODE WelsEventOpen (WELS_EVENT* event, const char* event_name) { |
| WELS_EVENT h = CreateEvent (NULL, FALSE, FALSE, NULL); |
| *event = h; |
| if (h == NULL) { |
| return WELS_THREAD_ERROR_GENERAL; |
| } |
| return WELS_THREAD_ERROR_OK; |
| } |
| |
| WELS_THREAD_ERROR_CODE WelsEventSignal (WELS_EVENT* event, WELS_MUTEX *pMutex, int* iCondition) { |
| (*iCondition) --; |
| if ((*iCondition) <= 0) { |
| if (SetEvent (*event)) { |
| return WELS_THREAD_ERROR_OK; |
| } |
| } |
| return WELS_THREAD_ERROR_GENERAL; |
| } |
| |
| WELS_THREAD_ERROR_CODE WelsEventWait (WELS_EVENT* event, WELS_MUTEX* pMutex, int& iCondition) { |
| return WaitForSingleObject (*event, INFINITE); |
| } |
| |
| WELS_THREAD_ERROR_CODE WelsEventWaitWithTimeOut (WELS_EVENT* event, uint32_t dwMilliseconds, WELS_MUTEX* pMutex) { |
| return WaitForSingleObject (*event, dwMilliseconds); |
| } |
| |
| WELS_THREAD_ERROR_CODE WelsMultipleEventsWaitSingleBlocking (uint32_t nCount, |
| WELS_EVENT* event_list, WELS_EVENT* master_even, WELS_MUTEX* pMutext) { |
| // Don't need/use the master event for anything, since windows has got WaitForMultipleObjects |
| return WaitForMultipleObjects (nCount, event_list, FALSE, INFINITE); |
| } |
| |
| WELS_THREAD_ERROR_CODE WelsEventClose (WELS_EVENT* event, const char* event_name) { |
| CloseHandle (*event); |
| |
| *event = NULL; |
| return WELS_THREAD_ERROR_OK; |
| } |
| |
| #ifndef WP80 |
| void WelsSleep (uint32_t dwMilliSecond) { |
| ::Sleep (dwMilliSecond); |
| } |
| #else |
| void WelsSleep (uint32_t dwMilliSecond) { |
| static WELS_EVENT hSleepEvent = NULL; |
| if (!hSleepEvent) { |
| WELS_EVENT hLocalSleepEvent = NULL; |
| WELS_THREAD_ERROR_CODE ret = WelsEventOpen (&hLocalSleepEvent); |
| if (WELS_THREAD_ERROR_OK != ret) { |
| return; |
| } |
| WELS_EVENT hPreviousEvent = InterlockedCompareExchangePointerRelease (&hSleepEvent, hLocalSleepEvent, NULL); |
| if (hPreviousEvent) { |
| WelsEventClose (&hLocalSleepEvent); |
| } |
| //On this singleton usage idea of using InterlockedCompareExchangePointerRelease: |
| // similar idea of can be found at msdn blog when introducing InterlockedCompareExchangePointerRelease |
| } |
| |
| WaitForSingleObject (hSleepEvent, dwMilliSecond); |
| } |
| #endif |
| |
| WELS_THREAD_ERROR_CODE WelsThreadCreate (WELS_THREAD_HANDLE* thread, LPWELS_THREAD_ROUTINE routine, |
| void* arg, WELS_THREAD_ATTR attr) { |
| WELS_THREAD_HANDLE h = CreateThread (NULL, 0, routine, arg, 0, NULL); |
| |
| if (h == NULL) { |
| return WELS_THREAD_ERROR_GENERAL; |
| } |
| * thread = h; |
| |
| return WELS_THREAD_ERROR_OK; |
| } |
| |
| WELS_THREAD_ERROR_CODE WelsThreadSetName (const char* thread_name) { |
| // do nothing |
| return WELS_THREAD_ERROR_OK; |
| } |
| |
| |
| WELS_THREAD_ERROR_CODE WelsThreadJoin (WELS_THREAD_HANDLE thread) { |
| WaitForSingleObject (thread, INFINITE); |
| CloseHandle (thread); |
| |
| return WELS_THREAD_ERROR_OK; |
| } |
| |
| |
| WELS_THREAD_HANDLE WelsThreadSelf() { |
| return GetCurrentThread(); |
| } |
| |
| WELS_THREAD_ERROR_CODE WelsQueryLogicalProcessInfo (WelsLogicalProcessInfo* pInfo) { |
| SYSTEM_INFO si; |
| |
| GetSystemInfo (&si); |
| |
| pInfo->ProcessorCount = si.dwNumberOfProcessors; |
| |
| return WELS_THREAD_ERROR_OK; |
| } |
| |
| #else //platform: #ifdef _WIN32 |
| |
| WELS_THREAD_ERROR_CODE WelsThreadCreate (WELS_THREAD_HANDLE* thread, LPWELS_THREAD_ROUTINE routine, |
| void* arg, WELS_THREAD_ATTR attr) { |
| WELS_THREAD_ERROR_CODE err = 0; |
| |
| pthread_attr_t at; |
| err = pthread_attr_init (&at); |
| if (err) |
| return err; |
| #if !defined(__ANDROID__) && !defined(__Fuchsia__) |
| err = pthread_attr_setscope (&at, PTHREAD_SCOPE_SYSTEM); |
| if (err) |
| return err; |
| err = pthread_attr_setschedpolicy (&at, SCHED_FIFO); |
| if (err) |
| return err; |
| #endif |
| err = pthread_create (thread, &at, routine, arg); |
| |
| pthread_attr_destroy (&at); |
| |
| return err; |
| } |
| |
| WELS_THREAD_ERROR_CODE WelsThreadSetName (const char* thread_name) { |
| #ifdef APPLE_IOS |
| pthread_setname_np (thread_name); |
| #endif |
| #if defined(__ANDROID__) && __ANDROID_API__ >= 9 |
| pthread_setname_np (pthread_self(), thread_name); |
| #endif |
| // do nothing |
| return WELS_THREAD_ERROR_OK; |
| } |
| |
| WELS_THREAD_ERROR_CODE WelsThreadJoin (WELS_THREAD_HANDLE thread) { |
| return pthread_join (thread, NULL); |
| } |
| |
| WELS_THREAD_HANDLE WelsThreadSelf() { |
| return pthread_self(); |
| } |
| |
| // unnamed semaphores aren't supported on OS X |
| |
| WELS_THREAD_ERROR_CODE WelsEventOpen (WELS_EVENT* p_event, const char* event_name) { |
| #ifdef __APPLE__ |
| WELS_THREAD_ERROR_CODE err= pthread_cond_init (p_event, NULL); |
| return err; |
| #else |
| WELS_EVENT event = (WELS_EVENT) malloc (sizeof (*event)); |
| if (event == NULL){ |
| *p_event = NULL; |
| return WELS_THREAD_ERROR_GENERAL; |
| } |
| WELS_THREAD_ERROR_CODE err = sem_init (event, 0, 0); |
| if (!err) { |
| *p_event = event; |
| return err; |
| } |
| free (event); |
| *p_event = NULL; |
| return err; |
| #endif |
| } |
| WELS_THREAD_ERROR_CODE WelsEventClose (WELS_EVENT* event, const char* event_name) { |
| //printf("event_close:%x, %s\n", event, event_name); |
| #ifdef __APPLE__ |
| WELS_THREAD_ERROR_CODE err = pthread_cond_destroy (event); |
| return err; |
| #else |
| WELS_THREAD_ERROR_CODE err = sem_destroy (*event); // match with sem_init |
| free (*event); |
| *event = NULL; |
| return err; |
| #endif |
| } |
| |
| void WelsSleep (uint32_t dwMilliSecond) { |
| usleep (dwMilliSecond * 1000); |
| } |
| |
| WELS_THREAD_ERROR_CODE WelsEventSignal (WELS_EVENT* event, WELS_MUTEX *pMutex, int* iCondition) { |
| WELS_THREAD_ERROR_CODE err = 0; |
| //fprintf( stderr, "before signal it, event=%x iCondition= %d..\n", event, *iCondition ); |
| #ifdef __APPLE__ |
| WelsMutexLock (pMutex); |
| (*iCondition) --; |
| WelsMutexUnlock (pMutex); |
| if ((*iCondition) <= 0) { |
| err = pthread_cond_signal (event); |
| //fprintf( stderr, "signal it, event=%x iCondition= %d..\n",event, *iCondition ); |
| |
| } |
| #else |
| (*iCondition) --; |
| if ((*iCondition) <= 0) { |
| // int32_t val = 0; |
| // sem_getvalue(event, &val); |
| // fprintf( stderr, "before signal it, val= %d..\n",val ); |
| if (event != NULL) |
| err = sem_post (*event); |
| // sem_getvalue(event, &val); |
| //fprintf( stderr, "signal it, event=%x iCondition= %d..\n",event, *iCondition ); |
| } |
| #endif |
| //fprintf( stderr, "after signal it, event=%x iCondition= %d..\n",event, *iCondition ); |
| return err; |
| } |
| |
| WELS_THREAD_ERROR_CODE WelsEventWait (WELS_EVENT* event, WELS_MUTEX* pMutex, int& iCondition) { |
| #ifdef __APPLE__ |
| int err = 0; |
| WelsMutexLock(pMutex); |
| //fprintf( stderr, "WelsEventWait event %x %d..\n", event, iCondition ); |
| while (iCondition>0) { |
| err = pthread_cond_wait (event, pMutex); |
| } |
| WelsMutexUnlock(pMutex); |
| return err; |
| #else |
| return sem_wait (*event); // blocking until signaled |
| #endif |
| } |
| |
| WELS_THREAD_ERROR_CODE WelsEventWaitWithTimeOut (WELS_EVENT* event, uint32_t dwMilliseconds, WELS_MUTEX* pMutex) { |
| |
| if (dwMilliseconds != (uint32_t) - 1) { |
| #if defined(__APPLE__) |
| return pthread_cond_wait (event, pMutex); |
| #else |
| return sem_wait (*event); |
| #endif |
| } else { |
| struct timespec ts; |
| struct timeval tv; |
| |
| gettimeofday (&tv, 0); |
| |
| ts.tv_nsec = tv.tv_usec * 1000 + dwMilliseconds * 1000000; |
| ts.tv_sec = tv.tv_sec + ts.tv_nsec / 1000000000; |
| ts.tv_nsec %= 1000000000; |
| |
| #if defined(__APPLE__) |
| return pthread_cond_timedwait (event, pMutex, &ts); |
| #else |
| return sem_timedwait (*event, &ts); |
| #endif |
| } |
| |
| } |
| |
| WELS_THREAD_ERROR_CODE WelsMultipleEventsWaitSingleBlocking (uint32_t nCount, |
| WELS_EVENT* event_list, WELS_EVENT* master_event, WELS_MUTEX* pMutex) { |
| uint32_t nIdx = 0; |
| uint32_t uiAccessTime = 2; // 2 us once |
| |
| if (nCount == 0) |
| return WELS_THREAD_ERROR_WAIT_FAILED; |
| #if defined(__APPLE__) |
| if (master_event != NULL) { |
| // This design relies on the events actually being semaphores; |
| // if multiple events in the list have been signalled, the master |
| // event should have a similar count (events in windows can't keep |
| // track of the actual count, but the master event isn't needed there |
| // since it uses WaitForMultipleObjects). |
| int32_t err = pthread_cond_wait (master_event, pMutex); |
| if (err != WELS_THREAD_ERROR_OK) |
| return err; |
| uiAccessTime = 0; // no blocking, just quickly loop through all to find the one that was signalled |
| } |
| |
| while (1) { |
| nIdx = 0; // access each event by order |
| while (nIdx < nCount) { |
| int32_t err = 0; |
| int32_t wait_count = 0; |
| |
| /* |
| * although such interface is not used in __GNUC__ like platform, to use |
| * pthread_cond_timedwait() might be better choice if need |
| */ |
| do { |
| err = pthread_cond_wait (&event_list[nIdx], pMutex); |
| if (WELS_THREAD_ERROR_OK == err) |
| return WELS_THREAD_ERROR_WAIT_OBJECT_0 + nIdx; |
| else if (wait_count > 0 || uiAccessTime == 0) |
| break; |
| usleep (uiAccessTime); |
| ++ wait_count; |
| } while (1); |
| // we do need access next event next time |
| ++ nIdx; |
| } |
| usleep (1); // switch to working threads |
| if (master_event != NULL) { |
| // A master event was used and was signalled, but none of the events in the |
| // list was found to be signalled, thus wait a little more when rechecking |
| // the list to avoid busylooping here. |
| // If we ever hit this codepath it's mostly a bug in the code that signals |
| // the events. |
| uiAccessTime = 2; |
| } |
| } |
| #else |
| if (master_event != NULL) { |
| // This design relies on the events actually being semaphores; |
| // if multiple events in the list have been signalled, the master |
| // event should have a similar count (events in windows can't keep |
| // track of the actual count, but the master event isn't needed there |
| // since it uses WaitForMultipleObjects). |
| int32_t err = sem_wait (*master_event); |
| if (err != WELS_THREAD_ERROR_OK) |
| return err; |
| uiAccessTime = 0; // no blocking, just quickly loop through all to find the one that was signalled |
| } |
| |
| while (1) { |
| nIdx = 0; // access each event by order |
| while (nIdx < nCount) { |
| int32_t err = 0; |
| int32_t wait_count = 0; |
| |
| /* |
| * although such interface is not used in __GNUC__ like platform, to use |
| * pthread_cond_timedwait() might be better choice if need |
| */ |
| do { |
| err = sem_trywait (event_list[nIdx]); |
| if (WELS_THREAD_ERROR_OK == err) |
| return WELS_THREAD_ERROR_WAIT_OBJECT_0 + nIdx; |
| else if (wait_count > 0 || uiAccessTime == 0) |
| break; |
| usleep (uiAccessTime); |
| ++ wait_count; |
| } while (1); |
| // we do need access next event next time |
| ++ nIdx; |
| } |
| usleep (1); // switch to working threads |
| if (master_event != NULL) { |
| // A master event was used and was signalled, but none of the events in the |
| // list was found to be signalled, thus wait a little more when rechecking |
| // the list to avoid busylooping here. |
| // If we ever hit this codepath it's mostly a bug in the code that signals |
| // the events. |
| uiAccessTime = 2; |
| } |
| } |
| |
| #endif |
| return WELS_THREAD_ERROR_WAIT_FAILED; |
| } |
| |
| WELS_THREAD_ERROR_CODE WelsQueryLogicalProcessInfo (WelsLogicalProcessInfo* pInfo) { |
| #ifdef ANDROID_NDK |
| pInfo->ProcessorCount = android_getCpuCount(); |
| return WELS_THREAD_ERROR_OK; |
| #elif defined(__linux__) || defined(__GNU__) |
| |
| cpu_set_t cpuset; |
| |
| CPU_ZERO (&cpuset); |
| |
| if (!sched_getaffinity (0, sizeof (cpuset), &cpuset)) { |
| #ifdef CPU_COUNT |
| pInfo->ProcessorCount = CPU_COUNT (&cpuset); |
| #else |
| int32_t count = 0; |
| for (int i = 0; i < CPU_SETSIZE; i++) { |
| if (CPU_ISSET (i, &cpuset)) { |
| count++; |
| } |
| } |
| pInfo->ProcessorCount = count; |
| #endif |
| } else { |
| pInfo->ProcessorCount = 1; |
| } |
| |
| return WELS_THREAD_ERROR_OK; |
| |
| #elif defined(__EMSCRIPTEN__) |
| |
| // There is not yet a way to determine CPU count in emscripten JS environment. |
| pInfo->ProcessorCount = emscripten_num_logical_cores(); |
| return WELS_THREAD_ERROR_OK; |
| |
| #elif defined(__Fuchsia__) |
| |
| pInfo->ProcessorCount = sysconf(_SC_NPROCESSORS_ONLN); |
| return WELS_THREAD_ERROR_OK; |
| #else |
| |
| size_t len = sizeof (pInfo->ProcessorCount); |
| |
| #if defined(__OpenBSD__) |
| int scname[] = { CTL_HW, HW_NCPUONLINE }; |
| if (sysctl (scname, 2, &pInfo->ProcessorCount, &len, NULL, 0) == -1) |
| #else |
| if (sysctlbyname (HW_NCPU_NAME, &pInfo->ProcessorCount, &len, NULL, 0) == -1) |
| #endif |
| pInfo->ProcessorCount = 1; |
| |
| return WELS_THREAD_ERROR_OK; |
| |
| #endif//__linux__ |
| } |
| |
| #endif |