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(<v, 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