openbcm

Git mirror of https://github.com/Broadcom-Network-Switching-Software/OpenBCM
git clone git://git.finwo.net/mirror/broadcom/openbcm
Log | Files | Refs | README

sync.c (22252B)


      1 /*
      2  * 
      3  * This license is set out in https://raw.githubusercontent.com/Broadcom-Network-Switching-Software/OpenBCM/master/Legal/LICENSE file.
      4  * 
      5  * Copyright 2007-2019 Broadcom Inc. All rights reserved.
      6  *
      7  * File: 	sync.c
      8  * Purpose:	Defines sal routines for mutexes and semaphores
      9  *
     10  * Mutex and Binary Semaphore abstraction
     11  *
     12  * Note: the SAL mutex abstraction is required to allow the same mutex
     13  * to be taken recursively by the same thread without deadlock.
     14  *
     15  * The POSIX mutex used here has been further abstracted to ensure this.
     16  */
     17 
     18 #if defined(LINUX) || defined(linux) || defined(__linux__)
     19 #define _XOPEN_SOURCE 600
     20 #endif
     21 
     22 #include <sys/types.h>
     23 #include <stdio.h>
     24 #include <stdlib.h>
     25 #include <errno.h>
     26 #include <signal.h>
     27 #include <unistd.h>
     28 #include <signal.h>
     29 #include <pthread.h>
     30 #include <semaphore.h>
     31 #include <time.h>
     32 #include <sys/time.h>
     33 
     34 #include <assert.h>
     35 #include <sal/core/sync.h>
     36 #include <sal/core/thread.h>
     37 #include <sal/core/time.h>
     38 #include <sal/core/libc.h>
     39 
     40 #include <soc/dnxc/multithread_analyzer.h>
     41 
     42 #ifndef BCM_MONOTONIC_MUTEXES
     43 
     44 #if defined(LINUX) && defined(__USE_UNIX98)
     45 
     46 #define USE_POSIX_RECURSIVE_MUTEX
     47 
     48 #endif
     49 
     50 
     51 #if defined(_POSIX_TIMERS) && (_POSIX_TIMERS >= 200112L) && \
     52         defined(_POSIX_TIMEOUTS) && (_POSIX_TIMEOUTS >= 200112L)
     53 /* Sometimes customers want to set new real time, so they must use monotic time.
     54   * But if we define USE_POSIX_SEM_TIMEDWAIT or USE_POSIX_MUTEX_TIMEDLOCK,
     55   * we will use real time, so we add this new Micro */
     56 #if !defined(BCM_MONOTONIC_TIME)
     57 #define USE_POSIX_SEM_TIMEDWAIT
     58 #define USE_POSIX_MUTEX_TIMEDLOCK
     59 #endif /*BCM_MONOTONIC_TIMER*/
     60 #endif
     61 
     62 #ifndef USE_POSIX_SPINLOCK
     63 #if defined(LINUX) && defined(__USE_XOPEN2K) && defined(__GLIBC__)
     64 #define USE_POSIX_SPINLOCK 1
     65 #else
     66 #define USE_POSIX_SPINLOCK 0
     67 #endif
     68 #endif
     69 
     70 #if defined (__STRICT_ANSI__)
     71 #define NO_CONTROL_C
     72 #endif
     73 
     74 #ifndef SECOND_NSEC
     75 #define SECOND_NSEC     (SECOND_USEC * 1000)
     76 #endif
     77 
     78 #ifdef BROADCOM_DEBUG
     79 #ifdef INCLUDE_BCM_SAL_PROFILE
     80 static unsigned int _sal_sem_count_curr;
     81 static unsigned int _sal_sem_count_max;
     82 static unsigned int _sal_mutex_count_curr;
     83 static unsigned int _sal_mutex_count_max;
     84 #define SAL_SEM_RESOURCE_USAGE_INCR(a_curr, a_max, ilock)               \
     85         a_curr++;                                                       \
     86         a_max = ((a_curr) > (a_max)) ? (a_curr) : (a_max)
     87     
     88 #define SAL_SEM_RESOURCE_USAGE_DECR(a_curr, ilock)                      \
     89         a_curr--
     90 
     91 /*
     92  * Function:
     93  *      sal_sem_resource_usage_get
     94  * Purpose:
     95  *      Provides count of active sem and maximum sem allocation
     96  * Parameters:
     97  *      sem_curr - Current semaphore allocation.
     98  *      sem_max - Maximum semaphore allocation.
     99  */
    100 
    101 void
    102 sal_sem_resource_usage_get(unsigned int *sem_curr, unsigned int *sem_max)
    103 {
    104     if (sem_curr != NULL) {
    105         *sem_curr = _sal_sem_count_curr;
    106     }
    107     if (sem_max != NULL) {
    108         *sem_max = _sal_sem_count_max;
    109     }
    110 }
    111 
    112 /*
    113  * Function:
    114  *      sal_mutex_resource_usage_get
    115  * Purpose:
    116  *      Provides count of active mutex and maximum mutex allocation
    117  * Parameters:
    118  *      mutex_curr - Current mutex allocation.
    119  *      mutex_max - Maximum mutex allocation.
    120  */
    121 
    122 void
    123 sal_mutex_resource_usage_get(unsigned int *mutex_curr, unsigned int *mutex_max)
    124 {
    125     if (mutex_curr != NULL) {
    126         *mutex_curr = _sal_mutex_count_curr;
    127     }
    128     if (mutex_max != NULL) {
    129         *mutex_max = _sal_mutex_count_max;
    130     }
    131 }
    132 #endif
    133 #endif
    134 
    135 /*
    136  * Keyboard interrupt protection
    137  *
    138  *   When a thread is running on a console, the user could Control-C
    139  *   while a mutex is held by the thread.  Control-C results in a signal
    140  *   that longjmp's somewhere else.  We prevent this from happening by
    141  *   blocking Control-C signals while any mutex is held.
    142  */
    143 
    144 #ifndef NO_CONTROL_C
    145 static int ctrl_c_depth = 0;
    146 int ctrl_c_blocked = 0;
    147 static sigset_t ctrl_c_sigset;
    148 static int ctrl_c_initialized;
    149 static sal_thread_t main_thread = SAL_THREAD_ERROR;
    150 #endif
    151 
    152 static void
    153 ctrl_c_block(void)
    154 {
    155 #ifndef NO_CONTROL_C
    156     if (main_thread == SAL_THREAD_ERROR) {
    157         main_thread = sal_thread_main_get();
    158     }
    159     if (sal_thread_self() == main_thread) {
    160 	if (ctrl_c_depth++ == 0) {
    161 	    sigprocmask(SIG_BLOCK, &ctrl_c_sigset, NULL);
    162         ctrl_c_blocked = 1;
    163 	}
    164     }
    165 #endif    
    166 }
    167 
    168 static void
    169 ctrl_c_unblock(void)
    170 {
    171 #ifndef NO_CONTROL_C
    172     if (main_thread == SAL_THREAD_ERROR) {
    173         main_thread = sal_thread_main_get();
    174     }
    175     if (sal_thread_self() == main_thread) {
    176 	assert(ctrl_c_depth > 0);
    177 	if (--ctrl_c_depth == 0) {
    178 	    sigprocmask(SIG_UNBLOCK, &ctrl_c_sigset, NULL);
    179         ctrl_c_blocked = 0;
    180 	}
    181     }
    182 #endif
    183 }
    184 
    185 #if defined(USE_POSIX_SEM_TIMEDWAIT) || defined(USE_POSIX_MUTEX_TIMEDLOCK)
    186 static
    187 int
    188 _sal_compute_timeout(struct timespec *ts, int usec)
    189 {
    190     int sec;
    191     uint32 nsecs;
    192 
    193 #ifdef CLOCK_REALTIME
    194     if (clock_gettime(CLOCK_REALTIME, ts) == 0) {
    195         ;
    196     }
    197     else
    198 #endif
    199     {
    200         struct timeval	ltv;
    201 
    202         /* Fall back to RTC if realtime clock unavailable */
    203         gettimeofday(&ltv, 0);
    204         ts->tv_sec = ltv.tv_sec;
    205         ts->tv_nsec = ltv.tv_usec * 1000;
    206     }
    207     /* Add in the delay */
    208     ts->tv_sec += usec / SECOND_USEC;
    209 
    210     /* compute new nsecs */
    211     nsecs = ts->tv_nsec + (usec % SECOND_USEC) * 1000;
    212 
    213     /* detect and handle rollover */
    214     if (nsecs < ts->tv_nsec) {
    215         ts->tv_sec += 1;
    216         nsecs -= SECOND_NSEC;
    217     }
    218     ts->tv_nsec = nsecs;
    219 
    220     /* Normalize if needed */
    221     sec = ts->tv_nsec / SECOND_NSEC;
    222     if (sec) {
    223         ts->tv_sec += sec;
    224         ts->tv_nsec = ts->tv_nsec % SECOND_NSEC;
    225     }
    226 
    227     /* indicate that we successfully got the time */
    228     return 1;
    229 }
    230 #endif
    231 
    232 /*
    233  * recursive_mutex_t
    234  *
    235  *   This is an abstract type built on the POSIX mutex that allows a
    236  *   mutex to be taken recursively by the same thread without deadlock.
    237  *
    238  *   The Linux version of pthreads supports recursive mutexes
    239  *   (a non-portable extension to posix). In this case, we 
    240  *   use the Linux support instead of our own. 
    241  */
    242 
    243 typedef struct recursive_mutex_s {
    244     pthread_mutex_t	mutex;
    245     char		*desc;
    246 #ifndef USE_POSIX_RECURSIVE_MUTEX
    247     sal_thread_t	owner;
    248     int			recurse_count;
    249 #endif
    250 
    251 #ifdef BROADCOM_DEBUG_MUTEX
    252 #define FILE_LOC_NAME_MAX 128
    253     unsigned int ctrl_c_blk;
    254     unsigned int take_count;
    255     unsigned int give_count;
    256     unsigned int tk_exc_gv_ind;
    257     char         prev_file_tk_location[FILE_LOC_NAME_MAX];
    258     char         last_file_tk_location[FILE_LOC_NAME_MAX];
    259 
    260     char         prev_file_gv_location[FILE_LOC_NAME_MAX];
    261     char         last_file_gv_location[FILE_LOC_NAME_MAX];
    262 #endif
    263 
    264 } recursive_mutex_t;
    265 
    266 
    267 #ifdef BROADCOM_DEBUG_MUTEX
    268 
    269 #include "string.h"
    270 
    271 #define MUTEX_DBG_ARR_MAX 5000
    272 static recursive_mutex_t *mutex_dbg_arr_ptr[MUTEX_DBG_ARR_MAX] = {0};
    273 
    274 /*If you want to set GDB on a breakpoint for certain events */
    275 void sal_mutext_dbg_break(void)
    276 {
    277     printf("\nGot Debugger Break Function Indication. Examine Call Stack Now\n");
    278 }
    279 
    280 int sal_mutex_take_intern(sal_mutex_t m, int usec);
    281 int sal_mutex_give_intern(sal_mutex_t m);
    282 
    283 int sal_mutex_take_bcm_debug(sal_mutex_t m, int usec, const char *take_loc, int line)
    284 {
    285     char lineNum[8] = {'\0'};
    286     int retVal;
    287     recursive_mutex_t	*rm = (recursive_mutex_t *) m;
    288     assert(rm);
    289 
    290     retVal = sal_mutex_take_intern(m, usec);
    291 
    292     rm->take_count++;
    293 
    294     sprintf(lineNum, ":%d", line);
    295 	strncpy(rm->prev_file_tk_location, rm->last_file_tk_location, FILE_LOC_NAME_MAX - 6);
    296 	strncpy(rm->last_file_tk_location, take_loc, FILE_LOC_NAME_MAX - 6);
    297     strcat(rm->last_file_tk_location, lineNum); 
    298 
    299    /* Detect recursion usage and flag*/
    300     if (rm->take_count > rm->give_count + 1 && !rm->tk_exc_gv_ind) {
    301         rm->tk_exc_gv_ind = 1; /* Only print first occurence */
    302         printf ("\nMTX TK:%d EXCEEDS GV:%d\n", rm->take_count, rm->give_count);
    303         printf ("AT: Last Loc:%s  Prev Loc: %s",
    304                 rm->last_file_tk_location, rm->prev_file_tk_location);
    305         sal_mutext_dbg_break();
    306     }
    307 
    308     return retVal;
    309 }
    310 
    311 
    312 int sal_mutex_give_bcm_debug(sal_mutex_t m, const char *give_loc, int line)
    313 {
    314     char lineNum[8] = {'\0'};
    315     recursive_mutex_t	*rm = (recursive_mutex_t *) m;
    316     assert(rm);
    317     rm->give_count++;
    318 
    319     sprintf(lineNum, ":%d", line);
    320 	strncpy(rm->prev_file_gv_location, rm->last_file_gv_location, FILE_LOC_NAME_MAX - 6);
    321 	strncpy(rm->last_file_gv_location, give_loc, FILE_LOC_NAME_MAX - 6);
    322     strcat(rm->last_file_gv_location, lineNum);
    323 
    324     if (rm->give_count > rm->take_count) {
    325         printf ("\nERROR: MTX GV:%d EXCEEDS TK:%d\n", rm->give_count, rm->take_count);
    326         sal_mutext_dbg_break();
    327     }
    328 
    329     return sal_mutex_give_intern(m);
    330 }
    331 
    332 
    333 void sal_mutex_dbg_dump(void)
    334 {
    335     int i = 0;
    336     char temp_ch;
    337 
    338     for (i = 0; i < MUTEX_DBG_ARR_MAX; i++) {
    339         /* Find nonempty slots */
    340         if (mutex_dbg_arr_ptr[i] != 0) {
    341             recursive_mutex_t *rm = mutex_dbg_arr_ptr[i];
    342 
    343 
    344             /* If MUTEX was created but never taken or given don't display it */
    345             if (rm->take_count || rm->give_count) {
    346                 printf ("\n\nMUTEX STATS[%d]  For:%s  Owner:ox%x CTRL_C_Depth:%d\n", 
    347                         i, rm->desc, (unsigned int)rm->owner, rm->ctrl_c_blk);
    348 
    349                 printf("\nT_CNT:%d G_CNT:%d \nPREV_T_LOC:%s  LST_T_LOC:%s\nPREV_G_LOC:%s  LST_G_LOC:%s", 
    350                        rm->take_count, rm->give_count,
    351                        rm->prev_file_tk_location, rm->last_file_tk_location,
    352                        rm->prev_file_gv_location, rm->last_file_gv_location);
    353             }
    354 
    355             if (rm->ctrl_c_blk > 0) {
    356                 printf ("\nWARNING: CTRL_C Left DISABLED??");
    357                 temp_ch = getchar(); /*Pause  */
    358             }
    359  
    360             if (rm->take_count != rm->give_count) {
    361                 printf ("\nWARNING: Take != Give");
    362                 temp_ch = getchar(); /*Pause  */
    363             }
    364         }
    365     }
    366 }
    367 
    368 
    369 
    370 #endif
    371 
    372 
    373 #ifdef netbsd
    374 /*
    375  * The netbsd pthreads implementation we are using
    376  * does not seem to have his function
    377  */
    378 static int
    379 pthread_mutexattr_init(pthread_mutexattr_t* attr)
    380 {
    381     attr->m_type = PTHREAD_MUTEXTYPE_DEBUG;
    382     attr->m_flags = 0;
    383     return 0;
    384 }
    385 
    386 #endif /* netbsd */
    387 
    388 static sal_mutex_t
    389 _sal_mutex_create(char *desc)
    390 {
    391     recursive_mutex_t	*rm;
    392     pthread_mutexattr_t attr;
    393 
    394 #ifdef BROADCOM_DEBUG_MUTEX
    395     int i = 0;
    396 #endif
    397 
    398 #ifndef NO_CONTROL_C
    399     if (!ctrl_c_initialized) {
    400         sigemptyset(&ctrl_c_sigset);
    401         sigaddset(&ctrl_c_sigset, SIGINT);
    402         ctrl_c_initialized = 1;
    403     }
    404 #endif
    405     
    406     if ((rm = malloc(sizeof (recursive_mutex_t))) == NULL) {
    407 	return NULL;
    408     }
    409 
    410     rm->desc = desc;
    411     pthread_mutexattr_init(&attr);
    412 #ifdef USE_POSIX_RECURSIVE_MUTEX
    413     pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
    414 #else
    415     rm->owner = 0;
    416     rm->recurse_count = 0;
    417 
    418 #ifdef BROADCOM_DEBUG_MUTEX
    419     rm->ctrl_c_blk = 0;
    420     rm->take_count = 0;
    421     rm->give_count = 0;
    422     rm->tk_exc_gv_ind = 0;
    423 
    424     for (i = 0; i < FILE_LOC_NAME_MAX; i++) {
    425         rm->prev_file_tk_location[i] = '\0';
    426         rm->last_file_tk_location[i] = '\0';
    427 
    428         rm->prev_file_gv_location[i] = '\0';
    429         rm->last_file_gv_location[i] = '\0';
    430     }
    431 
    432     for (i = 0; i < MUTEX_DBG_ARR_MAX; i++) {
    433         /* Find an empty slot */
    434         if (mutex_dbg_arr_ptr[i] == 0) {
    435             mutex_dbg_arr_ptr[i] = rm;
    436             break;
    437         }
    438     }
    439 
    440 #endif
    441 
    442 #endif
    443 
    444     pthread_mutex_init(&rm->mutex, &attr);
    445 
    446 #ifdef BROADCOM_DEBUG
    447 #ifdef INCLUDE_BCM_SAL_PROFILE
    448         SAL_SEM_RESOURCE_USAGE_INCR(
    449             _sal_mutex_count_curr,
    450             _sal_mutex_count_max,
    451             ilock);
    452 #endif
    453 #endif
    454     return (sal_mutex_t) rm;
    455 }
    456 
    457 /*
    458  * Mutex and semaphore abstraction
    459  */
    460 
    461 sal_mutex_t
    462 sal_mutex_create(char *desc)
    463 {
    464 #ifdef SAL_GLOBAL_MUTEX
    465     static sal_mutex_t _m = NULL;
    466     if (!_m) {
    467 	_m = _sal_mutex_create("sal_global_mutex");
    468 	assert(_m);
    469     }
    470     if (strcmp(desc, "spl mutex")) {
    471 	return _m;
    472     }
    473 #endif
    474     return _sal_mutex_create(desc);
    475 
    476 }
    477 
    478 void
    479 sal_mutex_destroy(sal_mutex_t m)
    480 {
    481     recursive_mutex_t	*rm = (recursive_mutex_t *) m;
    482 
    483 #ifdef BROADCOM_DEBUG_MUTEX
    484     int i;
    485 #endif
    486 
    487     assert(rm);
    488 
    489 #ifndef USE_POSIX_RECURSIVE_MUTEX
    490     /* Check for pending mutex unlocks */
    491     if ((rm->recurse_count > 0) || (rm->owner != 0)) {
    492         char thread_name[SAL_THREAD_NAME_MAX_LEN];
    493         sal_thread_name(rm->owner, thread_name, sizeof (thread_name));
    494         printf("WARNING: Mutex \"%s\" has not been unlocked before being destroyed.\n",
    495                         rm->desc);
    496         printf("\t Current owner is \"%s\"(%p) \n", thread_name, (void*)rm->owner);
    497     }
    498 #ifdef SAL_MUTEX_DEBUG
    499     assert(rm->recurse_count == 0);
    500     assert(rm->owner == 0);
    501 #endif /* SAL_MUTEX_DEBUG */
    502 
    503 #ifdef BROADCOM_DEBUG_MUTEX
    504     assert(rm->take_count == rm->give_count);
    505 
    506     for (i = 0; i < FILE_LOC_NAME_MAX; i++) {
    507         rm->prev_file_tk_location[i] = '\0';
    508         rm->last_file_tk_location[i] = '\0';
    509 
    510         rm->prev_file_gv_location[i] = '\0';
    511         rm->last_file_gv_location[i] = '\0';
    512     }
    513 
    514     for (i = 0; i < MUTEX_DBG_ARR_MAX; i++) {
    515         /* Find it's slot and mark empty*/
    516         if (mutex_dbg_arr_ptr[i] == rm) {
    517             mutex_dbg_arr_ptr[i] = 0;
    518             break;
    519         }
    520     }
    521 
    522 #endif
    523 
    524 #endif /* !USE_POSIX_RECURSIVE_MUTEX */
    525 
    526     pthread_mutex_destroy(&rm->mutex);
    527 
    528     free(rm);
    529 
    530 #ifdef BROADCOM_DEBUG
    531 #ifdef INCLUDE_BCM_SAL_PROFILE
    532         SAL_SEM_RESOURCE_USAGE_DECR(
    533             _sal_mutex_count_curr,
    534             ilock);
    535 #endif
    536 #endif
    537 }
    538 
    539 int
    540 #ifdef BROADCOM_DEBUG_MUTEX
    541 sal_mutex_take_intern(sal_mutex_t m, int usec)
    542 #else
    543 sal_mutex_take(sal_mutex_t m, int usec)
    544 #endif
    545 {
    546     recursive_mutex_t	*rm = (recursive_mutex_t *) m;
    547     int			err = 0;
    548 
    549 #ifndef USE_POSIX_RECURSIVE_MUTEX
    550     sal_thread_t	myself = sal_thread_self();
    551 #endif
    552 
    553 #ifdef USE_POSIX_MUTEX_TIMEDLOCK
    554     struct timespec	ts;
    555 #endif
    556 
    557     assert(rm);
    558 
    559     
    560     DNXC_MTA(dnxc_multithread_analyzer_declare_api_in_play(0, rm->desc, MTA_FLAG_MUTEX, 1));
    561 
    562 #ifndef USE_POSIX_RECURSIVE_MUTEX
    563     if (rm->owner == myself) {
    564 
    565         rm->recurse_count++;
    566 
    567         return 0;
    568     }
    569 
    570 #endif
    571 
    572     ctrl_c_block();
    573 
    574 #ifdef BROADCOM_DEBUG_MUTEX
    575     rm->ctrl_c_blk++;
    576 #endif
    577 
    578     if (usec == sal_mutex_FOREVER) {
    579         do {
    580             err = pthread_mutex_lock(&rm->mutex);
    581         } while (err != 0 && errno == EINTR);
    582     }
    583 
    584 #ifdef USE_POSIX_MUTEX_TIMEDLOCK
    585     else if (_sal_compute_timeout(&ts, usec)) {
    586         /* Treat EAGAIN as a fatal error on Linux */
    587         err = pthread_mutex_timedlock(&rm->mutex, &ts);
    588     }
    589 #else
    590     else {
    591         int		time_wait = 1;
    592 
    593         /* Retry algorithm with exponential backoff */
    594 
    595         for (;;) {
    596             err = pthread_mutex_trylock(&rm->mutex);
    597 
    598             if (err != EBUSY) {
    599                 break;		/* Done (0), or error other than EBUSY */
    600             }
    601 
    602             if (time_wait > usec) {
    603                 time_wait = usec;
    604             }
    605 
    606             sal_usleep(time_wait);
    607 
    608             usec -= time_wait;
    609 
    610             if (usec == 0) {
    611                 err = ETIMEDOUT;
    612                 break;
    613             }
    614 
    615             if ((time_wait *= 2) > 100000) {
    616                 time_wait = 100000;
    617             }
    618         }
    619     }
    620 #endif
    621 
    622     if (err) {
    623         ctrl_c_unblock();
    624 
    625 #ifdef BROADCOM_DEBUG_MUTEX
    626         printf("\n\nERROR in TAKING MUTEX \n\n");
    627         rm->ctrl_c_blk--;
    628 #endif
    629         assert(usec != sal_mutex_FOREVER);
    630         
    631         return -1;
    632     }
    633 
    634 #ifndef USE_POSIX_RECURSIVE_MUTEX
    635     assert(rm->owner == 0);
    636     rm->owner = myself;
    637 #endif
    638  
    639     return 0;
    640 }
    641 
    642 
    643 
    644 int
    645 #ifdef BROADCOM_DEBUG_MUTEX
    646 sal_mutex_give_intern(sal_mutex_t m)
    647 #else
    648 sal_mutex_give(sal_mutex_t m)
    649 #endif
    650 {
    651     recursive_mutex_t	*rm = (recursive_mutex_t *) m;
    652     int			err;
    653 
    654     assert(rm);
    655 
    656     
    657     DNXC_MTA(dnxc_multithread_analyzer_declare_api_in_play(0, rm->desc, MTA_FLAG_MUTEX, 0));
    658 
    659 #ifndef USE_POSIX_RECURSIVE_MUTEX
    660     if ((rm->owner != sal_thread_self())) {
    661         assert(rm->owner == sal_thread_self());
    662     }
    663 
    664     if (rm->recurse_count > 0) {
    665         rm->recurse_count--;
    666         return 0;
    667     }
    668 
    669     rm->owner = 0;
    670 #endif
    671 
    672 
    673     err = pthread_mutex_unlock(&rm->mutex);
    674     ctrl_c_unblock();
    675 
    676 #ifdef BROADCOM_DEBUG_MUTEX
    677     rm->ctrl_c_blk--;
    678 #endif
    679 
    680     assert(err == 0);
    681 
    682     return err ? -1 : 0;
    683 }
    684 
    685 
    686 /*
    687  * Wrapper class to hold additional info
    688  * along with the semaphore.
    689  */
    690 typedef struct {
    691     sem_t       s;
    692     char        *desc;
    693     int         binary;
    694 } wrapped_sem_t;
    695 
    696 sal_sem_t
    697 sal_sem_create(char *desc, int binary, int initial_count)
    698 {
    699     wrapped_sem_t *s = NULL;
    700 
    701     if ((s = malloc(sizeof (wrapped_sem_t))) == NULL) {
    702         return NULL;
    703     }
    704 
    705     /* 
    706      * This is needed by some libraries with a bug requiring to zero sem_t before calling sem_init(),
    707      * even though this it is not required by the function description.
    708      * Threads using sem_timedwait() to maintain polling interval use 100% CPU if we not set the memory to zero SDK-77724 
    709      */ 
    710     sal_memset(s, 0, sizeof(wrapped_sem_t));
    711 
    712     sem_init(&s->s, 0, initial_count);
    713     s->desc = desc;
    714     s->binary = binary;
    715 
    716 #ifdef BROADCOM_DEBUG
    717 #ifdef INCLUDE_BCM_SAL_PROFILE
    718         SAL_SEM_RESOURCE_USAGE_INCR(
    719             _sal_sem_count_curr,
    720             _sal_sem_count_max,
    721             ilock);
    722 #endif
    723 #endif
    724 
    725     return (sal_sem_t) s;
    726 }
    727 
    728 void
    729 sal_sem_destroy(sal_sem_t b)
    730 {
    731     wrapped_sem_t *s = (wrapped_sem_t *) b;
    732 
    733     assert(s);
    734 
    735     sem_destroy(&s->s);
    736 
    737     free(s);
    738 
    739 #ifdef BROADCOM_DEBUG
    740 #ifdef INCLUDE_BCM_SAL_PROFILE
    741         SAL_SEM_RESOURCE_USAGE_DECR(
    742             _sal_sem_count_curr,
    743             ilock);
    744 #endif
    745 #endif
    746 }
    747 
    748 int
    749 sal_sem_take(sal_sem_t b, int usec)
    750 {
    751     wrapped_sem_t		*s = (wrapped_sem_t *) b;
    752     int			err = 0;
    753 #ifdef USE_POSIX_SEM_TIMEDWAIT
    754     struct timespec	ts;
    755 #endif
    756 
    757     if (s == NULL) {
    758         return 0;
    759     }
    760 
    761     if ((usec < 0) && (usec != sal_sem_FOREVER)) {
    762         /* Return error if negative timeout is specified */
    763         return -1;
    764     }
    765 
    766     if (usec == sal_sem_FOREVER) {
    767         do {
    768             err = sem_wait(&s->s);
    769         } while (err != 0 && errno == EINTR);
    770     }
    771 #ifdef USE_POSIX_SEM_TIMEDWAIT
    772     else if (_sal_compute_timeout(&ts, usec)) {
    773         while (1) {
    774             if (!sem_timedwait(&s->s, &ts)) {
    775                 err = 0;
    776                 break;
    777             }
    778             if (errno != EAGAIN && errno != EINTR) {
    779                 err = errno;
    780                 break;
    781             }
    782         }
    783     }
    784 #else
    785     else {
    786 	int		time_wait = 1;
    787 
    788 	/* Retry algorithm with exponential backoff */
    789 
    790 	for (;;) {
    791 	    if (sem_trywait(&s->s) == 0) {
    792 		err = 0;
    793 		break;
    794 	    }
    795 
    796 	    if (errno != EAGAIN && errno != EINTR) {
    797 		err = errno;
    798 		break;
    799 	    }
    800 
    801 	    if (time_wait > usec) {
    802 		time_wait = usec;
    803 	    }
    804 
    805 	    sal_usleep(time_wait);
    806 
    807 	    usec -= time_wait;
    808 
    809 	    if (usec == 0) {
    810 		err = ETIMEDOUT;
    811 		break;
    812 	    }
    813 
    814 #ifdef BCM_MONOTONIC_TIME
    815         /* To reduce CPU share for some threads */
    816         if ((time_wait *= 4) > 100000) {
    817             time_wait = 100000;
    818         }
    819 #else
    820         if ((time_wait *= 2) > 100000) {
    821             time_wait = 100000;
    822         }
    823 #endif
    824 	}
    825     }
    826 #endif
    827 
    828     return err ? -1 : 0;
    829 }
    830 
    831 int
    832 sal_sem_give(sal_sem_t b)
    833 {
    834     wrapped_sem_t       *s = (wrapped_sem_t *) b;
    835     int                 err = 0;
    836     int                 sem_val = 0;
    837 
    838     if (s == NULL) {
    839         return 0;
    840     }
    841 
    842     /* Binary sem only post if sem_val == 0 */
    843     if (s->binary) {
    844         /* Post sem on getvalue failure */
    845         sem_getvalue(&s->s, &sem_val);
    846         if (sem_val == 0) {
    847             err = sem_post(&s->s);
    848         }
    849     } else {
    850         err = sem_post(&s->s);
    851     }
    852 
    853     return err ? -1 : 0;
    854 }
    855 
    856 #endif /* BCM_MONOTONIC_MUTEXES */
    857 
    858 /*
    859  * spinlock_ctrl_t
    860  *
    861  *   This is an abstract type built on the POSIX spinlock.
    862  */
    863 
    864 #if USE_POSIX_SPINLOCK
    865 typedef struct spinlock_ctrl_s {
    866     pthread_spinlock_t spinlock;
    867     char *desc;
    868 } *spinlock_ctrl_t;
    869 #endif
    870 
    871 /*
    872  * Function:
    873  *  sal_spinlock_create
    874  * Purpose:
    875  *  Create a spinlock
    876  * Parameters:
    877  *  desc - spinlock description
    878  * Returns:
    879  *  The spinlock or NULL if creation failed
    880  */
    881 
    882 sal_spinlock_t
    883 sal_spinlock_create(char *desc)
    884 {
    885 #if USE_POSIX_SPINLOCK
    886     spinlock_ctrl_t sl = malloc(sizeof(*sl));
    887     int result;
    888 
    889     if (sl != NULL) {
    890         result = pthread_spin_init(&(sl->spinlock), PTHREAD_PROCESS_SHARED);
    891         if (result != 0) {
    892             free(sl);
    893             return (sal_spinlock_t)NULL;
    894         }
    895         sl->desc = desc;
    896     }
    897     return (sal_spinlock_t)sl;
    898 #else
    899     return (sal_spinlock_t)sal_mutex_create(desc);
    900 #endif
    901 }
    902 
    903 /*
    904  * Function:
    905  *  sal_spinlock_destroy
    906  * Purpose:
    907  *  Destroy a spinlock
    908  * Parameters:
    909  *  lock - spinlock to destroy
    910  * Returns:
    911  *  0 on success, error code on failure
    912  */
    913 
    914 int
    915 sal_spinlock_destroy(sal_spinlock_t lock)
    916 {
    917 #if USE_POSIX_SPINLOCK
    918     spinlock_ctrl_t sl = (spinlock_ctrl_t)lock;
    919     int result;
    920 
    921     assert(sl);
    922     result = pthread_spin_destroy(&sl->spinlock);
    923     free(sl);
    924     return result;
    925 #else
    926     sal_mutex_destroy((sal_mutex_t)lock);
    927     return 0;
    928 #endif
    929 }
    930 
    931 /*
    932  * Function:
    933  *  sal_spinlock_lock
    934  * Purpose:
    935  *  Obtains a spinlock
    936  * Parameters:
    937  *  lock - spninlock to obtain
    938  * Returns:
    939  *  0 on success, error code on failure
    940  */
    941 
    942 int
    943 sal_spinlock_lock(sal_spinlock_t lock)
    944 {
    945 #if USE_POSIX_SPINLOCK
    946     spinlock_ctrl_t sl = (spinlock_ctrl_t)lock;
    947     struct timeval tv;
    948 
    949     assert(sl);
    950     /* On some systems pthread_spin_lock() is unsafe and won't block preemption,
    951        which could lead to deadlock. So here pthread_spin_trylock() is used instead
    952        to avoid this kind of issue. */
    953     while (pthread_spin_trylock(&sl->spinlock)) {
    954         tv.tv_sec = 0;
    955         tv.tv_usec = SECOND_USEC / sysconf(_SC_CLK_TCK);
    956         select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &tv);
    957     }
    958     return 0;
    959 #else
    960     return sal_mutex_take((sal_mutex_t)lock, sal_mutex_FOREVER);
    961 #endif
    962 }
    963 
    964 /*
    965  * Function:
    966  *  sal_spinlock_unlock
    967  * Purpose:
    968  *  Releases a spinlock
    969  * Parameters:
    970  *  lock - spinlock to release
    971  * Returns:
    972  *  0 on success, error code on failure
    973  */
    974 
    975 int
    976 sal_spinlock_unlock(sal_spinlock_t lock)
    977 {
    978 #if USE_POSIX_SPINLOCK
    979     spinlock_ctrl_t sl = (spinlock_ctrl_t)lock;
    980 
    981     assert(sl);
    982     return pthread_spin_unlock(&sl->spinlock);
    983 #else
    984     return sal_mutex_give((sal_mutex_t)lock);
    985 #endif
    986 }
    987