| /*! | |
| * \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 |