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