Changeset 4359 for pjproject/trunk/pjlib/src/pj/lock.c
- Timestamp:
- Feb 21, 2013 11:18:36 AM (11 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
pjproject/trunk/pjlib/src/pj/lock.c
r3553 r4359 21 21 #include <pj/os.h> 22 22 #include <pj/assert.h> 23 #include <pj/log.h> 23 24 #include <pj/pool.h> 24 25 #include <pj/string.h> 25 26 #include <pj/errno.h> 26 27 28 #define THIS_FILE "lock.c" 27 29 28 30 typedef void LOCK_OBJ; … … 197 199 } 198 200 201 202 /****************************************************************************** 203 * Group lock 204 */ 205 206 /* Individual lock in the group lock */ 207 typedef struct grp_lock_item 208 { 209 PJ_DECL_LIST_MEMBER(struct grp_lock_item); 210 int prio; 211 pj_lock_t *lock; 212 213 } grp_lock_item; 214 215 /* Destroy callbacks */ 216 typedef struct grp_destroy_callback 217 { 218 PJ_DECL_LIST_MEMBER(struct grp_destroy_callback); 219 void *comp; 220 void (*handler)(void*); 221 } grp_destroy_callback; 222 223 #if PJ_GRP_LOCK_DEBUG 224 /* Store each add_ref caller */ 225 typedef struct grp_lock_ref 226 { 227 PJ_DECL_LIST_MEMBER(struct grp_lock_ref); 228 const char *file; 229 int line; 230 } grp_lock_ref; 231 #endif 232 233 /* The group lock */ 234 struct pj_grp_lock_t 235 { 236 pj_lock_t base; 237 238 pj_pool_t *pool; 239 pj_atomic_t *ref_cnt; 240 pj_lock_t *own_lock; 241 242 pj_thread_t *owner; 243 int owner_cnt; 244 245 grp_lock_item lock_list; 246 grp_destroy_callback destroy_list; 247 248 #if PJ_GRP_LOCK_DEBUG 249 grp_lock_ref ref_list; 250 grp_lock_ref ref_free_list; 251 #endif 252 }; 253 254 255 PJ_DEF(void) pj_grp_lock_config_default(pj_grp_lock_config *cfg) 256 { 257 pj_bzero(cfg, sizeof(*cfg)); 258 } 259 260 static void grp_lock_set_owner_thread(pj_grp_lock_t *glock) 261 { 262 if (!glock->owner) { 263 glock->owner = pj_thread_this(); 264 glock->owner_cnt = 1; 265 } else { 266 pj_assert(glock->owner == pj_thread_this()); 267 glock->owner_cnt++; 268 } 269 } 270 271 static void grp_lock_unset_owner_thread(pj_grp_lock_t *glock) 272 { 273 pj_assert(glock->owner == pj_thread_this()); 274 pj_assert(glock->owner_cnt > 0); 275 if (--glock->owner_cnt <= 0) { 276 glock->owner = NULL; 277 glock->owner_cnt = 0; 278 } 279 } 280 281 static pj_status_t grp_lock_acquire(LOCK_OBJ *p) 282 { 283 pj_grp_lock_t *glock = (pj_grp_lock_t*)p; 284 grp_lock_item *lck; 285 286 pj_assert(pj_atomic_get(glock->ref_cnt) > 0); 287 288 lck = glock->lock_list.next; 289 while (lck != &glock->lock_list) { 290 pj_lock_acquire(lck->lock); 291 lck = lck->next; 292 } 293 grp_lock_set_owner_thread(glock); 294 pj_grp_lock_add_ref(glock); 295 return PJ_SUCCESS; 296 } 297 298 static pj_status_t grp_lock_tryacquire(LOCK_OBJ *p) 299 { 300 pj_grp_lock_t *glock = (pj_grp_lock_t*)p; 301 grp_lock_item *lck; 302 303 pj_assert(pj_atomic_get(glock->ref_cnt) > 0); 304 305 lck = glock->lock_list.next; 306 while (lck != &glock->lock_list) { 307 pj_status_t status = pj_lock_tryacquire(lck->lock); 308 if (status != PJ_SUCCESS) { 309 lck = lck->prev; 310 while (lck != &glock->lock_list) { 311 pj_lock_release(lck->lock); 312 lck = lck->prev; 313 } 314 return status; 315 } 316 lck = lck->next; 317 } 318 grp_lock_set_owner_thread(glock); 319 pj_grp_lock_add_ref(glock); 320 return PJ_SUCCESS; 321 } 322 323 static pj_status_t grp_lock_release(LOCK_OBJ *p) 324 { 325 pj_grp_lock_t *glock = (pj_grp_lock_t*)p; 326 grp_lock_item *lck; 327 328 grp_lock_unset_owner_thread(glock); 329 330 lck = glock->lock_list.prev; 331 while (lck != &glock->lock_list) { 332 pj_lock_release(lck->lock); 333 lck = lck->prev; 334 } 335 return pj_grp_lock_dec_ref(glock); 336 } 337 338 static pj_status_t grp_lock_destroy(LOCK_OBJ *p) 339 { 340 pj_grp_lock_t *glock = (pj_grp_lock_t*)p; 341 pj_pool_t *pool = glock->pool; 342 grp_lock_item *lck; 343 grp_destroy_callback *cb; 344 345 if (!glock->pool) { 346 /* already destroyed?! */ 347 return PJ_EINVAL; 348 } 349 350 /* Release all chained locks */ 351 lck = glock->lock_list.next; 352 while (lck != &glock->lock_list) { 353 if (lck->lock != glock->own_lock) { 354 unsigned i; 355 for (i=0; i<glock->owner_cnt; ++i) 356 pj_lock_release(lck->lock); 357 } 358 lck = lck->next; 359 } 360 361 /* Call callbacks */ 362 cb = glock->destroy_list.next; 363 while (cb != &glock->destroy_list) { 364 grp_destroy_callback *next = cb->next; 365 cb->handler(cb->comp); 366 cb = next; 367 } 368 369 pj_lock_destroy(glock->own_lock); 370 pj_atomic_destroy(glock->ref_cnt); 371 glock->pool = NULL; 372 pj_pool_release(pool); 373 374 return PJ_SUCCESS; 375 } 376 377 378 PJ_DEF(pj_status_t) pj_grp_lock_create( pj_pool_t *pool, 379 const pj_grp_lock_config *cfg, 380 pj_grp_lock_t **p_grp_lock) 381 { 382 pj_grp_lock_t *glock; 383 grp_lock_item *own_lock; 384 pj_status_t status; 385 386 PJ_ASSERT_RETURN(pool && p_grp_lock, PJ_EINVAL); 387 388 PJ_UNUSED_ARG(cfg); 389 390 pool = pj_pool_create(pool->factory, "glck%p", 512, 512, NULL); 391 if (!pool) 392 return PJ_ENOMEM; 393 394 glock = PJ_POOL_ZALLOC_T(pool, pj_grp_lock_t); 395 glock->base.lock_object = glock; 396 glock->base.acquire = &grp_lock_acquire; 397 glock->base.tryacquire = &grp_lock_tryacquire; 398 glock->base.release = &grp_lock_release; 399 glock->base.destroy = &grp_lock_destroy; 400 401 glock->pool = pool; 402 pj_list_init(&glock->lock_list); 403 pj_list_init(&glock->destroy_list); 404 #if PJ_GRP_LOCK_DEBUG 405 pj_list_init(&glock->ref_list); 406 pj_list_init(&glock->ref_free_list); 407 #endif 408 409 status = pj_atomic_create(pool, 0, &glock->ref_cnt); 410 if (status != PJ_SUCCESS) 411 goto on_error; 412 413 status = pj_lock_create_recursive_mutex(pool, pool->obj_name, 414 &glock->own_lock); 415 if (status != PJ_SUCCESS) 416 goto on_error; 417 418 own_lock = PJ_POOL_ZALLOC_T(pool, grp_lock_item); 419 own_lock->lock = glock->own_lock; 420 pj_list_push_back(&glock->lock_list, own_lock); 421 422 *p_grp_lock = glock; 423 return PJ_SUCCESS; 424 425 on_error: 426 grp_lock_destroy(glock); 427 return status; 428 } 429 430 PJ_DEF(pj_status_t) pj_grp_lock_destroy( pj_grp_lock_t *grp_lock) 431 { 432 return grp_lock_destroy(grp_lock); 433 } 434 435 PJ_DEF(pj_status_t) pj_grp_lock_acquire( pj_grp_lock_t *grp_lock) 436 { 437 return grp_lock_acquire(grp_lock); 438 } 439 440 PJ_DEF(pj_status_t) pj_grp_lock_tryacquire( pj_grp_lock_t *grp_lock) 441 { 442 return grp_lock_tryacquire(grp_lock); 443 } 444 445 PJ_DEF(pj_status_t) pj_grp_lock_release( pj_grp_lock_t *grp_lock) 446 { 447 return grp_lock_release(grp_lock); 448 } 449 450 PJ_DEF(pj_status_t) pj_grp_lock_replace( pj_grp_lock_t *old, 451 pj_grp_lock_t *new) 452 { 453 grp_destroy_callback *ocb; 454 455 /* Move handlers from old to new */ 456 ocb = old->destroy_list.next; 457 while (ocb != &old->destroy_list) { 458 grp_destroy_callback *ncb; 459 460 ncb = PJ_POOL_ALLOC_T(new->pool, grp_destroy_callback); 461 ncb->comp = ocb->comp; 462 ncb->handler = ocb->handler; 463 pj_list_push_back(&new->destroy_list, ncb); 464 465 ocb = ocb->next; 466 } 467 468 pj_list_init(&old->destroy_list); 469 470 grp_lock_destroy(old); 471 return PJ_SUCCESS; 472 } 473 474 PJ_DEF(pj_status_t) pj_grp_lock_add_handler( pj_grp_lock_t *glock, 475 pj_pool_t *pool, 476 void *comp, 477 void (*destroy)(void *comp)) 478 { 479 grp_destroy_callback *cb; 480 481 grp_lock_acquire(glock); 482 483 if (pool == NULL) 484 pool = glock->pool; 485 486 cb = PJ_POOL_ZALLOC_T(pool, grp_destroy_callback); 487 cb->comp = comp; 488 cb->handler = destroy; 489 pj_list_push_back(&glock->destroy_list, cb); 490 491 grp_lock_release(glock); 492 return PJ_SUCCESS; 493 } 494 495 PJ_DEF(pj_status_t) pj_grp_lock_del_handler( pj_grp_lock_t *glock, 496 void *comp, 497 void (*destroy)(void *comp)) 498 { 499 grp_destroy_callback *cb; 500 501 grp_lock_acquire(glock); 502 503 cb = glock->destroy_list.next; 504 while (cb != &glock->destroy_list) { 505 if (cb->comp == comp && cb->handler == destroy) 506 break; 507 cb = cb->next; 508 } 509 510 if (cb != &glock->destroy_list) 511 pj_list_erase(cb); 512 513 grp_lock_release(glock); 514 return PJ_SUCCESS; 515 } 516 517 static pj_status_t grp_lock_add_ref(pj_grp_lock_t *glock) 518 { 519 pj_atomic_inc(glock->ref_cnt); 520 return PJ_SUCCESS; 521 } 522 523 static pj_status_t grp_lock_dec_ref(pj_grp_lock_t *glock) 524 { 525 int cnt; /* for debugging */ 526 if ((cnt=pj_atomic_dec_and_get(glock->ref_cnt)) == 0) { 527 grp_lock_destroy(glock); 528 return PJ_EGONE; 529 } 530 pj_assert(cnt > 0); 531 pj_grp_lock_dump(glock); 532 return PJ_SUCCESS; 533 } 534 535 #if PJ_GRP_LOCK_DEBUG 536 PJ_DEF(pj_status_t) pj_grp_lock_add_ref_dbg(pj_grp_lock_t *glock, 537 const char *file, 538 int line) 539 { 540 grp_lock_ref *ref; 541 pj_status_t status; 542 543 pj_enter_critical_section(); 544 if (!pj_list_empty(&glock->ref_free_list)) { 545 ref = glock->ref_free_list.next; 546 pj_list_erase(ref); 547 } else { 548 ref = PJ_POOL_ALLOC_T(glock->pool, grp_lock_ref); 549 } 550 551 ref->file = file; 552 ref->line = line; 553 pj_list_push_back(&glock->ref_list, ref); 554 555 pj_leave_critical_section(); 556 557 status = grp_lock_add_ref(glock); 558 559 if (status != PJ_SUCCESS) { 560 pj_enter_critical_section(); 561 pj_list_erase(ref); 562 pj_list_push_back(&glock->ref_free_list, ref); 563 pj_leave_critical_section(); 564 } 565 566 return status; 567 } 568 569 PJ_DEF(pj_status_t) pj_grp_lock_dec_ref_dbg(pj_grp_lock_t *glock, 570 const char *file, 571 int line) 572 { 573 grp_lock_ref *ref; 574 575 pj_enter_critical_section(); 576 /* Find the same source file */ 577 ref = glock->ref_list.next; 578 while (ref != &glock->ref_list) { 579 if (strcmp(ref->file, file) == 0) { 580 pj_list_erase(ref); 581 pj_list_push_back(&glock->ref_free_list, ref); 582 break; 583 } 584 ref = ref->next; 585 } 586 pj_leave_critical_section(); 587 588 if (ref == &glock->ref_list) { 589 PJ_LOG(2,(THIS_FILE, "pj_grp_lock_dec_ref_dbg() could not find " 590 "matching ref for %s", file)); 591 } 592 593 return grp_lock_dec_ref(glock); 594 } 595 #else 596 PJ_DEF(pj_status_t) pj_grp_lock_add_ref(pj_grp_lock_t *glock) 597 { 598 return grp_lock_add_ref(glock); 599 } 600 601 PJ_DEF(pj_status_t) pj_grp_lock_dec_ref(pj_grp_lock_t *glock) 602 { 603 return grp_lock_dec_ref(glock); 604 } 605 #endif 606 607 PJ_DEF(int) pj_grp_lock_get_ref(pj_grp_lock_t *glock) 608 { 609 return pj_atomic_get(glock->ref_cnt); 610 } 611 612 PJ_DEF(pj_status_t) pj_grp_lock_chain_lock( pj_grp_lock_t *glock, 613 pj_lock_t *lock, 614 int pos) 615 { 616 grp_lock_item *lck, *new_lck; 617 unsigned i; 618 619 grp_lock_acquire(glock); 620 621 for (i=0; i<glock->owner_cnt; ++i) 622 pj_lock_acquire(lock); 623 624 lck = glock->lock_list.next; 625 while (lck != &glock->lock_list) { 626 if (lck->prio >= pos) 627 break; 628 lck = lck->next; 629 } 630 631 new_lck = PJ_POOL_ZALLOC_T(glock->pool, grp_lock_item); 632 new_lck->prio = pos; 633 new_lck->lock = lock; 634 pj_list_insert_before(lck, new_lck); 635 636 /* this will also release the new lock */ 637 grp_lock_release(glock); 638 return PJ_SUCCESS; 639 } 640 641 PJ_DEF(pj_status_t) pj_grp_lock_unchain_lock( pj_grp_lock_t *glock, 642 pj_lock_t *lock) 643 { 644 grp_lock_item *lck; 645 646 grp_lock_acquire(glock); 647 648 lck = glock->lock_list.next; 649 while (lck != &glock->lock_list) { 650 if (lck->lock == lock) 651 break; 652 lck = lck->next; 653 } 654 655 if (lck != &glock->lock_list) { 656 unsigned i; 657 658 pj_list_erase(lck); 659 for (i=0; i<glock->owner_cnt; ++i) 660 pj_lock_release(lck->lock); 661 } 662 663 grp_lock_release(glock); 664 return PJ_SUCCESS; 665 } 666 667 PJ_DEF(void) pj_grp_lock_dump(pj_grp_lock_t *grp_lock) 668 { 669 #if PJ_GRP_LOCK_DEBUG 670 grp_lock_ref *ref = grp_lock->ref_list.next; 671 char info_buf[1000]; 672 pj_str_t info; 673 674 info.ptr = info_buf; 675 info.slen = 0; 676 677 pj_grp_lock_acquire(grp_lock); 678 pj_enter_critical_section(); 679 680 while (ref != &grp_lock->ref_list && info.slen < sizeof(info_buf)) { 681 char *start = info.ptr + info.slen; 682 int max_len = sizeof(info_buf) - info.slen; 683 int len; 684 685 len = pj_ansi_snprintf(start, max_len, "%s:%d ", ref->file, ref->line); 686 if (len < 1 || len > max_len) { 687 len = strlen(ref->file); 688 if (len > max_len - 1) 689 len = max_len - 1; 690 691 memcpy(start, ref->file, len); 692 start[len++] = ' '; 693 } 694 695 info.slen += len; 696 697 ref = ref->next; 698 } 699 700 if (ref != &grp_lock->ref_list) { 701 int i; 702 for (i=0; i<4; ++i) 703 info_buf[sizeof(info_buf)-i-1] = '.'; 704 } 705 info.ptr[info.slen-1] = '\0'; 706 707 pj_leave_critical_section(); 708 pj_grp_lock_release(grp_lock); 709 710 PJ_LOG(4,(THIS_FILE, "Group lock %p, ref_cnt=%d. Reference holders: %s", 711 grp_lock, pj_grp_lock_get_ref(grp_lock), info.ptr)); 712 #endif 713 }
Note: See TracChangeset
for help on using the changeset viewer.